2012-09-13 23:48:02

by David Howells

[permalink] [raw]
Subject: [RFC][PATCH 00/16] Asymmetric / Public-key cryptography key type


Hi Herbert, Rusty,

I've redone my crypto keys patches to be more specific, implementing an
asymmetric key type for containing the stuff required for public-key
cryptography and anything else one might want an asymmetric key for.

This facility can be used for module signing, firmware signing and eCryptfs.

The patches can be found here as well as following this covering note:

http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=shortlog;h=refs/heads/crypto-keys-post-KS

I have arranged things such that:

(1) Keys of the asymmetric key type are just simple containers. They have
three attributes:

(a) A subtype.

(b) The data for the subtype. This is entirely defined by the subtype.

(c) A binary identifier (in addition to the usual text decription) that
can also be used to match the key.

(2) The asymmetric key subtype provides the mechanism used to operate with the
key. The mechanism can be entirely in-kernel, or can be offloaded to
hardware.

A completely software subtype is provided that can support a multiplicity
of public-key algorithms. One such algorithm is currently provided (RSA).

(3) Parsers can be registered that can take the instantiation data and turn it
into a subtype pointer, appropriate subtype data, a binary identifier and
a proposed description (keys can be auto-described).

An X.509 certificate parser is provided. It can be given a certificate
like this:

keyctl padd asymmetric "" @s </tmp/x509.cert

giving something like:

102492b2 I--Q--- 1 perm 39390000 0 0 asymmetri bfbc0cd76d050ea4:/C=GB/L=Cambridge/O=Red Hat/CN=kernel key: X509.RSA 0c688c7b []

Note that the X.509 parser uses the ASN.1 decoder and fast OID registry
patches.

I have dropped the PGP key parser for the moment.

Parsers can be provided that interpret the instantiation data to be a
pointer to where the key is actually stored (eg. a hardware key store).
Examples might be:

keyctl add asymmetric "" "tpm:bfbc0cd76d050ea4" @s
keyctl add asymmetric "" "uefi:bfbc0cd76d050ea4" @s

The parser could then, for example, extract the key from a key store and
set up for the software subtype or it could use a subtype that offloads
crypto operations to the hardware.

(4) The main interface for using it is in include/crypto/public_key.h.
Currently, there is one operation function provided: verify_signature() -
it takes a key, the parsed-out signature data and the signed data digest
and indicates whether the signature is valid or not.

(5) The sources are under crypto/ rather than security/ as that's a better fit
for them.

I wonder if I should rename the key type to public_key (rather than
asymmetric). I'm not sure what else it might be used for.

David
---
David Howells (16):
X.509: Add a crypto key parser for binary (DER) X.509 certificates
MPILIB: Provide a function to read raw data into an MPI
X.509: Add an ASN.1 decoder
X.509: Add simple ASN.1 grammar compiler
X.509: Add utility functions to render OIDs as strings
X.509: Implement simple static OID registry
RSA: Fix signature verification for shorter signatures
RSA: Implement signature verification algorithm [PKCS#1 / RFC3447]
MPILIB: Reinstate mpi_cmp[_ui]() and export for RSA signature verification
KEYS: Provide signature verification with an asymmetric key
KEYS: Asymmetric public-key algorithm crypto key subtype
KEYS: Asymmetric key pluggable data parsers
KEYS: Implement asymmetric key type
KEYS: Document asymmetric key type
MPILIB: Provide count_leading/trailing_zeros() based on arch functions
KEYS: Add payload preparsing opportunity prior to key instantiate or update


Documentation/crypto/asymmetric-keys.txt | 312 ++++++
Documentation/security/keys.txt | 50 +
crypto/Kconfig | 1
crypto/Makefile | 1
crypto/asymmetric_keys/.gitignore | 1
crypto/asymmetric_keys/Kconfig | 38 +
crypto/asymmetric_keys/Makefile | 27 +
crypto/asymmetric_keys/asymmetric_keys.h | 15
crypto/asymmetric_keys/asymmetric_type.c | 274 +++++
crypto/asymmetric_keys/public_key.c | 108 ++
crypto/asymmetric_keys/public_key.h | 30 +
crypto/asymmetric_keys/rsa.c | 277 +++++
crypto/asymmetric_keys/signature.c | 49 +
crypto/asymmetric_keys/x509.asn1 | 60 +
crypto/asymmetric_keys/x509_cert_parser.c | 503 +++++++++
crypto/asymmetric_keys/x509_parser.h | 37 +
crypto/asymmetric_keys/x509_public_key.c | 206 ++++
crypto/asymmetric_keys/x509_rsakey.asn1 | 4
fs/cifs/cifs_spnego.c | 6
fs/cifs/cifsacl.c | 8
include/asm-generic/bitops/count_zeros.h | 57 +
include/crypto/public_key.h | 113 ++
include/keys/asymmetric-parser.h | 37 +
include/keys/asymmetric-subtype.h | 55 +
include/keys/asymmetric-type.h | 25
include/keys/user-type.h | 6
include/linux/asn1.h | 67 +
include/linux/asn1_ber_bytecode.h | 87 ++
include/linux/asn1_decoder.h | 24
include/linux/key-type.h | 35 +
include/linux/mpi.h | 1
include/linux/oid_registry.h | 92 ++
init/Kconfig | 8
lib/.gitignore | 2
lib/Kconfig | 5
lib/Makefile | 18
lib/asn1_decoder.c | 473 +++++++++
lib/build_OID_registry | 209 ++++
lib/mpi/Makefile | 1
lib/mpi/longlong.h | 138 ---
lib/mpi/mpi-bit.c | 2
lib/mpi/mpi-cmp.c | 70 +
lib/mpi/mpi-pow.c | 4
lib/mpi/mpicoder.c | 55 +
lib/oid_registry.c | 170 +++
net/ceph/crypto.c | 9
net/dns_resolver/dns_key.c | 6
net/rxrpc/ar-key.c | 40 -
scripts/.gitignore | 1
scripts/Makefile | 2
scripts/Makefile.build | 11
scripts/asn1_compiler.c | 1544 +++++++++++++++++++++++++++++
security/keys/encrypted-keys/encrypted.c | 16
security/keys/key.c | 114 ++
security/keys/keyctl.c | 18
security/keys/keyring.c | 6
security/keys/request_key_auth.c | 8
security/keys/trusted.c | 16
security/keys/user_defined.c | 14
59 files changed, 5324 insertions(+), 242 deletions(-)
create mode 100644 Documentation/crypto/asymmetric-keys.txt
create mode 100644 crypto/asymmetric_keys/.gitignore
create mode 100644 crypto/asymmetric_keys/Kconfig
create mode 100644 crypto/asymmetric_keys/Makefile
create mode 100644 crypto/asymmetric_keys/asymmetric_keys.h
create mode 100644 crypto/asymmetric_keys/asymmetric_type.c
create mode 100644 crypto/asymmetric_keys/public_key.c
create mode 100644 crypto/asymmetric_keys/public_key.h
create mode 100644 crypto/asymmetric_keys/rsa.c
create mode 100644 crypto/asymmetric_keys/signature.c
create mode 100644 crypto/asymmetric_keys/x509.asn1
create mode 100644 crypto/asymmetric_keys/x509_cert_parser.c
create mode 100644 crypto/asymmetric_keys/x509_parser.h
create mode 100644 crypto/asymmetric_keys/x509_public_key.c
create mode 100644 crypto/asymmetric_keys/x509_rsakey.asn1
create mode 100644 include/asm-generic/bitops/count_zeros.h
create mode 100644 include/crypto/public_key.h
create mode 100644 include/keys/asymmetric-parser.h
create mode 100644 include/keys/asymmetric-subtype.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/mpi/mpi-cmp.c
create mode 100644 lib/oid_registry.c
create mode 100644 scripts/asn1_compiler.c



2012-09-13 23:48:22

by David Howells

[permalink] [raw]
Subject: [PATCH 02/16] MPILIB: Provide count_leading/trailing_zeros() based on arch functions

Provide count_leading/trailing_zeros() macros based on extant arch bit scanning
functions rather than reimplementing from scratch in MPILIB.

Whilst we're at it, turn count_foo_zeros(n, x) into n = count_foo_zeros(x).

Also move the definition to asm-generic as other people may be interested in
using it.

Signed-off-by: David Howells <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: Dmitry Kasatkin <[email protected]>
Cc: Arnd Bergmann <[email protected]>
---

include/asm-generic/bitops/count_zeros.h | 57 ++++++++++++
lib/mpi/longlong.h | 138 ------------------------------
lib/mpi/mpi-bit.c | 2
lib/mpi/mpi-pow.c | 4 -
4 files changed, 62 insertions(+), 139 deletions(-)
create mode 100644 include/asm-generic/bitops/count_zeros.h


diff --git a/include/asm-generic/bitops/count_zeros.h b/include/asm-generic/bitops/count_zeros.h
new file mode 100644
index 0000000..97520d2
--- /dev/null
+++ b/include/asm-generic/bitops/count_zeros.h
@@ -0,0 +1,57 @@
+/* Count leading and trailing zeros functions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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 _ASM_GENERIC_BITOPS_COUNT_ZEROS_H_
+#define _ASM_GENERIC_BITOPS_COUNT_ZEROS_H_
+
+#include <asm/bitops.h>
+
+/**
+ * count_leading_zeros - Count the number of zeros from the MSB back
+ * @x: The value
+ *
+ * Count the number of leading zeros from the MSB going towards the LSB in @x.
+ *
+ * If the MSB of @x is set, the result is 0.
+ * If only the LSB of @x is set, then the result is BITS_PER_LONG-1.
+ * If @x is 0 then the result is COUNT_LEADING_ZEROS_0.
+ */
+static inline int count_leading_zeros(unsigned long x)
+{
+ if (sizeof(x) == 4)
+ return BITS_PER_LONG - fls(x);
+ else
+ return BITS_PER_LONG - fls64(x);
+}
+
+#define COUNT_LEADING_ZEROS_0 BITS_PER_LONG
+
+/**
+ * count_trailing_zeros - Count the number of zeros from the LSB forwards
+ * @x: The value
+ *
+ * Count the number of trailing zeros from the LSB going towards the MSB in @x.
+ *
+ * If the LSB of @x is set, the result is 0.
+ * If only the MSB of @x is set, then the result is BITS_PER_LONG-1.
+ * If @x is 0 then the result is COUNT_TRAILING_ZEROS_0.
+ */
+static inline int count_trailing_zeros(unsigned long x)
+{
+#define COUNT_TRAILING_ZEROS_0 (-1)
+
+ if (sizeof(x) == 4)
+ return ffs(x);
+ else
+ return (x != 0) ? __ffs(x) : COUNT_TRAILING_ZEROS_0;
+}
+
+#endif /* _ASM_GENERIC_BITOPS_COUNT_ZEROS_H_ */
diff --git a/lib/mpi/longlong.h b/lib/mpi/longlong.h
index 29f9862..678ce4f 100644
--- a/lib/mpi/longlong.h
+++ b/lib/mpi/longlong.h
@@ -19,6 +19,8 @@
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA. */

+#include <asm-generic/bitops/count_zeros.h>
+
/* You have to define the following before including this file:
*
* UWtype -- An unsigned type, default type for operations (typically a "word")
@@ -146,12 +148,6 @@ do { \
: "1" ((USItype)(n1)), \
"r" ((USItype)(n0)), \
"r" ((USItype)(d)))
-
-#define count_leading_zeros(count, x) \
- __asm__ ("clz %0,%1" \
- : "=r" ((USItype)(count)) \
- : "r" ((USItype)(x)))
-#define COUNT_LEADING_ZEROS_0 32
#endif /* __a29k__ */

#if defined(__alpha) && W_TYPE_SIZE == 64
@@ -298,11 +294,6 @@ extern UDItype __udiv_qrnnd();
: "1" ((USItype)(nh)), \
"0" ((USItype)(nl)), \
"g" ((USItype)(d)))
-#define count_leading_zeros(count, x) \
- __asm__ ("bsch/1 %1,%0" \
- : "=g" (count) \
- : "g" ((USItype)(x)), \
- "0" ((USItype)0))
#endif

/***************************************
@@ -354,27 +345,6 @@ do { USItype __r; \
} while (0)
extern USItype __udiv_qrnnd();
#endif /* LONGLONG_STANDALONE */
-#define count_leading_zeros(count, x) \
-do { \
- USItype __tmp; \
- __asm__ ( \
- "ldi 1,%0\n" \
- "extru,= %1,15,16,%%r0 ; Bits 31..16 zero?\n" \
- "extru,tr %1,15,16,%1 ; No. Shift down, skip add.\n" \
- "ldo 16(%0),%0 ; Yes. Perform add.\n" \
- "extru,= %1,23,8,%%r0 ; Bits 15..8 zero?\n" \
- "extru,tr %1,23,8,%1 ; No. Shift down, skip add.\n" \
- "ldo 8(%0),%0 ; Yes. Perform add.\n" \
- "extru,= %1,27,4,%%r0 ; Bits 7..4 zero?\n" \
- "extru,tr %1,27,4,%1 ; No. Shift down, skip add.\n" \
- "ldo 4(%0),%0 ; Yes. Perform add.\n" \
- "extru,= %1,29,2,%%r0 ; Bits 3..2 zero?\n" \
- "extru,tr %1,29,2,%1 ; No. Shift down, skip add.\n" \
- "ldo 2(%0),%0 ; Yes. Perform add.\n" \
- "extru %1,30,1,%1 ; Extract bit 1.\n" \
- "sub %0,%1,%0 ; Subtract it. " \
- : "=r" (count), "=r" (__tmp) : "1" (x)); \
-} while (0)
#endif /* hppa */

/***************************************
@@ -457,15 +427,6 @@ do { \
: "0" ((USItype)(n0)), \
"1" ((USItype)(n1)), \
"rm" ((USItype)(d)))
-#define count_leading_zeros(count, x) \
-do { \
- USItype __cbtmp; \
- __asm__ ("bsrl %1,%0" \
- : "=r" (__cbtmp) : "rm" ((USItype)(x))); \
- (count) = __cbtmp ^ 31; \
-} while (0)
-#define count_trailing_zeros(count, x) \
- __asm__ ("bsfl %1,%0" : "=r" (count) : "rm" ((USItype)(x)))
#ifndef UMUL_TIME
#define UMUL_TIME 40
#endif
@@ -536,15 +497,6 @@ do { \
"dI" ((USItype)(d))); \
(r) = __rq.__i.__l; (q) = __rq.__i.__h; \
} while (0)
-#define count_leading_zeros(count, x) \
-do { \
- USItype __cbtmp; \
- __asm__ ("scanbit %1,%0" \
- : "=r" (__cbtmp) \
- : "r" ((USItype)(x))); \
- (count) = __cbtmp ^ 31; \
-} while (0)
-#define COUNT_LEADING_ZEROS_0 (-32) /* sic */
#if defined(__i960mx) /* what is the proper symbol to test??? */
#define rshift_rhlc(r, h, l, c) \
do { \
@@ -603,11 +555,6 @@ do { \
: "0" ((USItype)(n0)), \
"1" ((USItype)(n1)), \
"dmi" ((USItype)(d)))
-#define count_leading_zeros(count, x) \
- __asm__ ("bfffo %1{%b2:%b2},%0" \
- : "=d" ((USItype)(count)) \
- : "od" ((USItype)(x)), "n" (0))
-#define COUNT_LEADING_ZEROS_0 32
#else /* not mc68020 */
#define umul_ppmm(xh, xl, a, b) \
do { USItype __umul_tmp1, __umul_tmp2; \
@@ -664,15 +611,6 @@ do { USItype __umul_tmp1, __umul_tmp2; \
"rJ" ((USItype)(bh)), \
"rJ" ((USItype)(al)), \
"rJ" ((USItype)(bl)))
-#define count_leading_zeros(count, x) \
-do { \
- USItype __cbtmp; \
- __asm__ ("ff1 %0,%1" \
- : "=r" (__cbtmp) \
- : "r" ((USItype)(x))); \
- (count) = __cbtmp ^ 31; \
-} while (0)
-#define COUNT_LEADING_ZEROS_0 63 /* sic */
#if defined(__m88110__)
#define umul_ppmm(wh, wl, u, v) \
do { \
@@ -779,12 +717,6 @@ do { \
: "0" (__xx.__ll), \
"g" ((USItype)(d))); \
(r) = __xx.__i.__l; (q) = __xx.__i.__h; })
-#define count_trailing_zeros(count, x) \
-do { \
- __asm__("ffsd %2,%0" \
- : "=r"((USItype) (count)) \
- : "0"((USItype) 0), "r"((USItype) (x))); \
- } while (0)
#endif /* __ns32000__ */

/***************************************
@@ -855,11 +787,6 @@ do { \
"rI" ((USItype)(al)), \
"r" ((USItype)(bl))); \
} while (0)
-#define count_leading_zeros(count, x) \
- __asm__ ("{cntlz|cntlzw} %0,%1" \
- : "=r" ((USItype)(count)) \
- : "r" ((USItype)(x)))
-#define COUNT_LEADING_ZEROS_0 32
#if defined(_ARCH_PPC)
#define umul_ppmm(ph, pl, m0, m1) \
do { \
@@ -1001,19 +928,6 @@ do { \
} while (0)
#define UMUL_TIME 20
#define UDIV_TIME 200
-#define count_leading_zeros(count, x) \
-do { \
- if ((x) >= 0x10000) \
- __asm__ ("clz %0,%1" \
- : "=r" ((USItype)(count)) \
- : "r" ((USItype)(x) >> 16)); \
- else { \
- __asm__ ("clz %0,%1" \
- : "=r" ((USItype)(count)) \
- : "r" ((USItype)(x))); \
- (count) += 16; \
- } \
-} while (0)
#endif /* RT/ROMP */

/***************************************
@@ -1142,13 +1056,6 @@ do { \
"rI" ((USItype)(d)) \
: "%g1" __AND_CLOBBER_CC)
#define UDIV_TIME 37
-#define count_leading_zeros(count, x) \
- __asm__ ("scan %1,0,%0" \
- : "=r" ((USItype)(x)) \
- : "r" ((USItype)(count)))
-/* Early sparclites return 63 for an argument of 0, but they warn that future
- implementations might change this. Therefore, leave COUNT_LEADING_ZEROS_0
- undefined. */
#endif /* __sparclite__ */
#endif /* __sparc_v8__ */
/* Default to sparc v7 versions of umul_ppmm and udiv_qrnnd. */
@@ -1454,47 +1361,6 @@ do { \
#define udiv_qrnnd __udiv_qrnnd_c
#endif

-#undef count_leading_zeros
-#if !defined(count_leading_zeros)
- extern
-#ifdef __STDC__
- const
-#endif
- unsigned char __clz_tab[];
-#define count_leading_zeros(count, x) \
-do { \
- UWtype __xr = (x); \
- UWtype __a; \
- \
- if (W_TYPE_SIZE <= 32) { \
- __a = __xr < ((UWtype) 1 << 2*__BITS4) \
- ? (__xr < ((UWtype) 1 << __BITS4) ? 0 : __BITS4) \
- : (__xr < ((UWtype) 1 << 3*__BITS4) ? 2*__BITS4 : 3*__BITS4); \
- } \
- else { \
- for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8) \
- if (((__xr >> __a) & 0xff) != 0) \
- break; \
- } \
- \
- (count) = W_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a); \
-} while (0)
- /* This version gives a well-defined value for zero. */
-#define COUNT_LEADING_ZEROS_0 W_TYPE_SIZE
-#endif
-
-#if !defined(count_trailing_zeros)
-/* Define count_trailing_zeros using count_leading_zeros. The latter might be
- defined in asm, but if it is not, the C version above is good enough. */
-#define count_trailing_zeros(count, x) \
-do { \
- UWtype __ctz_x = (x); \
- UWtype __ctz_c; \
- count_leading_zeros(__ctz_c, __ctz_x & -__ctz_x); \
- (count) = W_TYPE_SIZE - 1 - __ctz_c; \
-} while (0)
-#endif
-
#ifndef UDIV_NEEDS_NORMALIZATION
#define UDIV_NEEDS_NORMALIZATION 0
#endif
diff --git a/lib/mpi/mpi-bit.c b/lib/mpi/mpi-bit.c
index 5687248..503537e 100644
--- a/lib/mpi/mpi-bit.c
+++ b/lib/mpi/mpi-bit.c
@@ -45,7 +45,7 @@ unsigned mpi_get_nbits(MPI a)
if (a->nlimbs) {
mpi_limb_t alimb = a->d[a->nlimbs - 1];
if (alimb)
- count_leading_zeros(n, alimb);
+ n = count_leading_zeros(alimb);
else
n = BITS_PER_MPI_LIMB;
n = BITS_PER_MPI_LIMB - n + (a->nlimbs - 1) * BITS_PER_MPI_LIMB;
diff --git a/lib/mpi/mpi-pow.c b/lib/mpi/mpi-pow.c
index 67f3e79..5464c87 100644
--- a/lib/mpi/mpi-pow.c
+++ b/lib/mpi/mpi-pow.c
@@ -77,7 +77,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod)
mp = mp_marker = mpi_alloc_limb_space(msize);
if (!mp)
goto enomem;
- count_leading_zeros(mod_shift_cnt, mod->d[msize - 1]);
+ mod_shift_cnt = count_leading_zeros(mod->d[msize - 1]);
if (mod_shift_cnt)
mpihelp_lshift(mp, mod->d, msize, mod_shift_cnt);
else
@@ -169,7 +169,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod)

i = esize - 1;
e = ep[i];
- count_leading_zeros(c, e);
+ c = count_leading_zeros(e);
e = (e << c) << 1; /* shift the exp bits to the left, lose msb */
c = BITS_PER_MPI_LIMB - 1 - c;


2012-09-13 23:48:31

by David Howells

[permalink] [raw]
Subject: [PATCH 03/16] KEYS: Document asymmetric key type

In-source documentation for the asymmetric key type. This will be located in:

Documentation/crypto/asymmetric-keys.txt

Signed-off-by: David Howells <[email protected]>
---

Documentation/crypto/asymmetric-keys.txt | 312 ++++++++++++++++++++++++++++++
1 file changed, 312 insertions(+)
create mode 100644 Documentation/crypto/asymmetric-keys.txt


diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt
new file mode 100644
index 0000000..b767590
--- /dev/null
+++ b/Documentation/crypto/asymmetric-keys.txt
@@ -0,0 +1,312 @@
+ =============================================
+ ASYMMETRIC / PUBLIC-KEY CRYPTOGRAPHY KEY TYPE
+ =============================================
+
+Contents:
+
+ - Overview.
+ - Key identification.
+ - Accessing asymmetric keys.
+ - Signature verification.
+ - Asymmetric key subtypes.
+ - Instantiation data parsers.
+
+
+========
+OVERVIEW
+========
+
+The "asymmetric" key type is designed to be a container for the keys used in
+public-key cryptography, without imposing any particular restrictions on the
+form or mechanism of the cryptography or form of the key.
+
+The asymmetric key is given a subtype that defines what sort of data is
+associated with the key and provides operations to describe and destroy it.
+However, no requirement is made that the key data actually be stored in the
+key.
+
+A completely in-kernel key retention and operation subtype can be defined, but
+it would also be possible to provide access to cryptographic hardware (such as
+a TPM) that might be used to both retain the relevant key and perform
+operations using that key. In such a case, the asymmetric key would then
+merely be an interface to the TPM driver.
+
+Also provided is the concept of a data parser. Data parsers are responsible
+for extracting information from the blobs of data passed to the instantiation
+function. The first data parser that recognises the blob gets to set the
+subtype of the key and define the operations that can be done on that key.
+
+A data parser may interpret the data blob as containing the bits representing a
+key, or it may interpret it as a reference to a key held somewhere else in the
+system (for example, a TPM).
+
+
+==================
+KEY IDENTIFICATION
+==================
+
+If a key is added with an empty name, the instantiation data parsers are given
+the opportunity to pre-parse a key and to determine the description the key
+should be given from the content of the key.
+
+This can then be used to refer to the key, either by complete match or by
+partial match. The key type may also use other criteria to refer to a key.
+
+The asymmetric key type's match function can then perform a wider range of
+comparisons than just the straightforward comparison of the description with
+the criterion string:
+
+ (1) If the criterion string is of the form "id:<hexdigits>" then the match
+ function will examine a key's fingerprint to see if the hex digits given
+ after the "id:" match the tail. For instance:
+
+ keyctl search @s asymmetric id:5acc2142
+
+ will match a key with fingerprint:
+
+ 1A00 2040 7601 7889 DE11 882C 3823 04AD 5ACC 2142
+
+ (2) If the criterion string is of the form "<subtype>:<hexdigits>" then the
+ match will match the ID as in (1), but with the added restriction that
+ only keys of the specified subtype (e.g. tpm) will be matched. For
+ instance:
+
+ keyctl search @s asymmetric tpm:5acc2142
+
+Looking in /proc/keys, the last 8 hex digits of the key fingerprint are
+displayed, along with the subtype:
+
+ 1a39e171 I----- 1 perm 3f010000 0 0 asymmetri modsign.0: DSA 5acc2142 []
+
+
+=========================
+ACCESSING ASYMMETRIC KEYS
+=========================
+
+For general access to asymmetric keys from within the kernel, the following
+inclusion is required:
+
+ #include <crypto/public_key.h>
+
+This gives access to functions for dealing with asymmetric / public keys.
+Three enums are defined there for representing public-key cryptography
+algorithms:
+
+ enum pkey_algo
+
+digest algorithms used by those:
+
+ enum pkey_hash_algo
+
+and key identifier representations:
+
+ enum pkey_id_type
+
+Note that the key type representation types are required because key
+identifiers from different standards aren't necessarily compatible. For
+instance, PGP generates key identifiers by hashing the key data plus some
+PGP-specific metadata, whereas X.509 has arbitrary certificate identifiers.
+
+The operations defined upon a key are:
+
+ (1) Signature verification.
+
+Other operations are possible (such as encryption) with the same key data
+required for verification, but not currently supported, and others
+(eg. decryption and signature generation) require extra key data.
+
+
+SIGNATURE VERIFICATION
+----------------------
+
+An operation is provided to perform cryptographic signature verification, using
+an asymmetric key to provide or to provide access to the public key.
+
+ int verify_signature(const struct key *key,
+ const struct public_key_signature *sig);
+
+The caller must have already obtained the key from some source and can then use
+it to check the signature. The caller must have parsed the signature and
+transferred the relevant bits to the structure pointed to by sig.
+
+ struct public_key_signature {
+ u8 *digest;
+ u8 digest_size;
+ enum pkey_hash_algo pkey_hash_algo : 8;
+ u8 nr_mpi;
+ union {
+ MPI mpi[2];
+ ...
+ };
+ };
+
+The algorithm used must be noted in sig->pkey_hash_algo, and all the MPIs that
+make up the actual signature must be stored in sig->mpi[] and the count of MPIs
+placed in sig->nr_mpi.
+
+In addition, the data must have been digested by the caller and the resulting
+hash must be pointed to by sig->digest and the size of the hash be placed in
+sig->digest_size.
+
+The function will return 0 upon success or -EKEYREJECTED if the signature
+doesn't match.
+
+The function may also return -ENOTSUPP if an unsupported public-key algorithm
+or public-key/hash algorithm combination is specified or the key doesn't
+support the operation; -EBADMSG or -ERANGE if some of the parameters have weird
+data; or -ENOMEM if an allocation can't be performed. -EINVAL can be returned
+if the key argument is the wrong type or is incompletely set up.
+
+
+=======================
+ASYMMETRIC KEY SUBTYPES
+=======================
+
+Asymmetric keys have a subtype that defines the set of operations that can be
+performed on that key and that determines what data is attached as the key
+payload. The payload format is entirely at the whim of the subtype.
+
+The subtype is selected by the key data parser and the parser must initialise
+the data required for it. The asymmetric key retains a reference on the
+subtype module.
+
+The subtype definition structure can be found in:
+
+ #include <keys/asymmetric-subtype.h>
+
+and looks like the following:
+
+ struct asymmetric_key_subtype {
+ struct module *owner;
+ const char *name;
+
+ void (*describe)(const struct key *key, struct seq_file *m);
+ void (*destroy)(void *payload);
+ int (*verify_signature)(const struct key *key,
+ const struct public_key_signature *sig);
+ };
+
+Asymmetric keys point to this with their type_data[0] member.
+
+The owner and name fields should be set to the owning module and the name of
+the subtype. Currently, the name is only used for print statements.
+
+There are a number of operations defined by the subtype:
+
+ (1) describe().
+
+ Mandatory. This allows the subtype to display something in /proc/keys
+ against the key. For instance the name of the public key algorithm type
+ could be displayed. The key type will display the tail of the key
+ identity string after this.
+
+ (2) destroy().
+
+ Mandatory. This should free the memory associated with the key. The
+ asymmetric key will look after freeing the fingerprint and releasing the
+ reference on the subtype module.
+
+ (3) verify_signature().
+
+ Optional. These are the entry points for the key usage operations.
+ Currently there is only the one defined. If not set, the caller will be
+ given -ENOTSUPP. The subtype may do anything it likes to implement an
+ operation, including offloading to hardware.
+
+
+==========================
+INSTANTIATION DATA PARSERS
+==========================
+
+The asymmetric key type doesn't generally want to store or to deal with a raw
+blob of data that holds the key data. It would have to parse it and error
+check it each time it wanted to use it. Further, the contents of the blob may
+have various checks that can be performed on it (eg. self-signatures, validity
+dates) and may contain useful data about the key (identifiers, capabilities).
+
+Also, the blob may represent a pointer to some hardware containing the key
+rather than the key itself.
+
+Examples of blob formats for which parsers could be implemented include:
+
+ - OpenPGP packet stream [RFC 4880].
+ - X.509 ASN.1 stream.
+ - Pointer to TPM key.
+ - Pointer to UEFI key.
+
+During key instantiation each parser in the list is tried until one doesn't
+return -EBADMSG.
+
+The parser definition structure can be found in:
+
+ #include <keys/asymmetric-parser.h>
+
+and looks like the following:
+
+ struct asymmetric_key_parser {
+ struct module *owner;
+ const char *name;
+
+ int (*parse)(struct key_preparsed_payload *prep);
+ };
+
+The owner and name fields should be set to the owning module and the name of
+the parser.
+
+There is currently only a single operation defined by the parser, and it is
+mandatory:
+
+ (1) parse().
+
+ This is called to preparse the key from the key creation and update paths.
+ In particular, it is called during the key creation _before_ a key is
+ allocated, and as such, is permitted to provide the key's description in
+ the case that the caller declines to do so.
+
+ The caller passes a pointer to the following struct with all of the fields
+ cleared, except for data, datalen and quotalen [see
+ Documentation/security/keys.txt].
+
+ struct key_preparsed_payload {
+ char *description;
+ void *type_data[2];
+ void *payload;
+ const void *data;
+ size_t datalen;
+ size_t quotalen;
+ };
+
+ The instantiation data is in a blob pointed to by data and is datalen in
+ size. The parse() function is not permitted to change these two values at
+ all, and shouldn't change any of the other values _unless_ they are
+ recognise the blob format and will not return -EBADMSG to indicate it is
+ not theirs.
+
+ If the parser is happy with the blob, it should propose a description for
+ the key and attach it to ->description, ->type_data[0] should be set to
+ point to the subtype to be used, ->payload should be set to point to the
+ initialised data for that subtype, ->type_data[1] should point to a hex
+ fingerprint and quotalen should be updated to indicate how much quota this
+ key should account for.
+
+ When clearing up, the data attached to ->type_data[1] and ->description
+ will be kfree()'d and the data attached to ->payload will be passed to the
+ subtype's ->destroy() method to be disposed of. A module reference for
+ the subtype pointed to by ->type_data[0] will be put.
+
+
+ If the data format is not recognised, -EBADMSG should be returned. If it
+ is recognised, but the key cannot for some reason be set up, some other
+ negative error code should be returned. On success, 0 should be returned.
+
+ The key's fingerprint string may be partially matched upon. For a
+ public-key algorithm such as RSA and DSA this will likely be a printable
+ hex version of the key's fingerprint.
+
+Functions are provided to register and unregister parsers:
+
+ int register_asymmetric_key_parser(struct asymmetric_key_parser *parser);
+ void unregister_asymmetric_key_parser(struct asymmetric_key_parser *subtype);
+
+Parsers may not have the same name. The names are otherwise only used for
+displaying in debugging messages.

2012-09-13 23:48:49

by David Howells

[permalink] [raw]
Subject: [PATCH 05/16] KEYS: Asymmetric key pluggable data parsers

The instantiation data passed to the asymmetric key type are expected to be
formatted in some way, and there are several possible standard ways to format
the data.

The two obvious standards are OpenPGP keys and X.509 certificates. The latter
is especially useful when dealing with UEFI, and the former might be useful
when dealing with, say, eCryptfs.

Further, it might be desirable to provide formatted blobs that indicate
hardware is to be accessed to retrieve the keys or that the keys live
unretrievably in a hardware store, but that the keys can be used by means of
the hardware.

>From userspace, the keys can be loaded using the keyctl command, for example,
an X.509 binary certificate:

keyctl padd asymmetric foo @s <dhowells.pem

or a PGP key:

keyctl padd asymmetric bar @s <dhowells.pub

or a pointer into the contents of the TPM:

keyctl add asymmetric zebra "TPM:04982390582905f8" @s

Inside the kernel, pluggable parsers register themselves and then get to
examine the payload data to see if they can handle it. If they can, they get
to:

(1) Propose a name for the key, to be used it the name is "" or NULL.

(2) Specify the key subtype.

(3) Provide the data for the subtype.

The key type asks the parser to do its stuff before a key is allocated and thus
before the name is set. If successful, the parser stores the suggested data
into the key_preparsed_payload struct, which will be either used (if the key is
successfully created and instantiated or updated) or discarded.

Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/asymmetric_type.c | 120 ++++++++++++++++++++++++++++++
include/keys/asymmetric-parser.h | 37 +++++++++
2 files changed, 156 insertions(+), 1 deletion(-)
create mode 100644 include/keys/asymmetric-parser.h


diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
index bfb0424..cf80765 100644
--- a/crypto/asymmetric_keys/asymmetric_type.c
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -11,6 +11,7 @@
* 2 of the Licence, or (at your option) any later version.
*/
#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -18,6 +19,9 @@

MODULE_LICENSE("GPL");

+static LIST_HEAD(asymmetric_key_parsers);
+static DECLARE_RWSEM(asymmetric_key_parsers_sem);
+
/*
* Match asymmetric keys on (part of) their name
* We have some shorthand methods for matching keys. We allow:
@@ -107,12 +111,79 @@ static void asymmetric_key_describe(const struct key *key, struct seq_file *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 preparse data
+ */
+static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
+{
+ struct asymmetric_key_subtype *subtype = prep->type_data[0];
+
+ pr_devel("==>%s()\n", __func__);
+
+ if (subtype) {
+ subtype->destroy(prep->payload);
+ module_put(subtype->owner);
+ }
+ kfree(prep->type_data[1]);
+ kfree(prep->description);
+}
+
+/*
* Instantiate a asymmetric_key defined key. The key was preparsed, so we just
* have to transfer the data here.
*/
static int asymmetric_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
{
- return -EOPNOTSUPP;
+ int ret;
+
+ pr_devel("==>%s()\n", __func__);
+
+ ret = key_payload_reserve(key, prep->quotalen);
+ if (ret == 0) {
+ key->type_data.p[0] = prep->type_data[0];
+ key->type_data.p[1] = prep->type_data[1];
+ key->payload.data = prep->payload;
+ prep->type_data[0] = NULL;
+ prep->type_data[1] = NULL;
+ prep->payload = NULL;
+ }
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
}

/*
@@ -132,6 +203,8 @@ static void asymmetric_key_destroy(struct key *key)

struct key_type key_type_asymmetric = {
.name = "asymmetric",
+ .preparse = asymmetric_key_preparse,
+ .free_preparse = asymmetric_key_free_preparse,
.instantiate = asymmetric_key_instantiate,
.match = asymmetric_key_match,
.destroy = asymmetric_key_destroy,
@@ -139,6 +212,51 @@ struct key_type key_type_asymmetric = {
};
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
*/
diff --git a/include/keys/asymmetric-parser.h b/include/keys/asymmetric-parser.h
new file mode 100644
index 0000000..09b3b48
--- /dev/null
+++ b/include/keys/asymmetric-parser.h
@@ -0,0 +1,37 @@
+/* Asymmetric public-key cryptography data parser
+ *
+ * See Documentation/crypto/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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 _KEYS_ASYMMETRIC_PARSER_H
+#define _KEYS_ASYMMETRIC_PARSER_H
+
+/*
+ * Key data parser. Called during key instantiation.
+ */
+struct asymmetric_key_parser {
+ struct list_head link;
+ struct module *owner;
+ const char *name;
+
+ /* Attempt to parse a key from the data blob passed to add_key() or
+ * keyctl_instantiate(). Should also generate a proposed description
+ * that the caller can optionally use for the key.
+ *
+ * Return EBADMSG if not recognised.
+ */
+ int (*parse)(struct key_preparsed_payload *prep);
+};
+
+extern int register_asymmetric_key_parser(struct asymmetric_key_parser *);
+extern void unregister_asymmetric_key_parser(struct asymmetric_key_parser *);
+
+#endif /* _KEYS_ASYMMETRIC_PARSER_H */

2012-09-13 23:48:12

by David Howells

[permalink] [raw]
Subject: [PATCH 01/16] KEYS: Add payload preparsing opportunity prior to key instantiate or update

Give the key type the opportunity to preparse the payload prior to the
instantiation and update routines being called. This is done with the
provision of two new key type operations:

int (*preparse)(struct key_preparsed_payload *prep);
void (*free_preparse)(struct key_preparsed_payload *prep);

If the first operation is present, then it is called before key creation (in
the add/update case) or before the key semaphore is taken (in the update and
instantiate cases). The second operation is called to clean up if the first
was called.

preparse() is given the opportunity to fill in the following structure:

struct key_preparsed_payload {
char *description;
void *type_data[2];
void *payload;
const void *data;
size_t datalen;
size_t quotalen;
};

Before the preparser is called, the first three fields will have been cleared,
the payload pointer and size will be stored in data and datalen and the default
quota size from the key_type struct will be stored into quotalen.

The preparser may parse the payload in any way it likes and may store data in
the type_data[] and payload fields for use by the instantiate() and update()
ops.

The preparser may also propose a description for the key by attaching it as a
string to the description field. This can be used by passing a NULL or ""
description to the add_key() system call or the key_create_or_update()
function. This cannot work with request_key() as that required the description
to tell the upcall about the key to be created.

This, for example permits keys that store PGP public keys to generate their own
name from the user ID and public key fingerprint in the key.

The instantiate() and update() operations are then modified to look like this:

int (*instantiate)(struct key *key, struct key_preparsed_payload *prep);
int (*update)(struct key *key, struct key_preparsed_payload *prep);

and the new payload data is passed in *prep, whether or not it was preparsed.

Signed-off-by: David Howells <[email protected]>
---

Documentation/security/keys.txt | 50 +++++++++++++
fs/cifs/cifs_spnego.c | 6 +-
fs/cifs/cifsacl.c | 8 +-
include/keys/user-type.h | 6 +-
include/linux/key-type.h | 35 +++++++++
net/ceph/crypto.c | 9 +-
net/dns_resolver/dns_key.c | 6 +-
net/rxrpc/ar-key.c | 40 +++++------
security/keys/encrypted-keys/encrypted.c | 16 ++--
security/keys/key.c | 114 ++++++++++++++++++++++--------
security/keys/keyctl.c | 18 ++++-
security/keys/keyring.c | 6 +-
security/keys/request_key_auth.c | 8 +-
security/keys/trusted.c | 16 ++--
security/keys/user_defined.c | 14 ++--
15 files changed, 250 insertions(+), 102 deletions(-)


diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt
index aa0dbd7..7d9ca92 100644
--- a/Documentation/security/keys.txt
+++ b/Documentation/security/keys.txt
@@ -412,6 +412,10 @@ The main syscalls are:
to the keyring. In this case, an error will be generated if the process
does not have permission to write to the keyring.

+ If the key type supports it, if the description is NULL or an empty
+ string, the key type will try and generate a description from the content
+ of the payload.
+
The payload is optional, and the pointer can be NULL if not required by
the type. The payload is plen in size, and plen can be zero for an empty
payload.
@@ -1114,12 +1118,53 @@ The structure has a number of fields, some of which are mandatory:
it should return 0.


- (*) int (*instantiate)(struct key *key, const void *data, size_t datalen);
+ (*) int (*preparse)(struct key_preparsed_payload *prep);
+
+ This optional method permits the key type to attempt to parse payload
+ before a key is created (add key) or the key semaphore is taken (update or
+ instantiate key). The structure pointed to by prep looks like:
+
+ struct key_preparsed_payload {
+ char *description;
+ void *type_data[2];
+ void *payload;
+ const void *data;
+ size_t datalen;
+ size_t quotalen;
+ };
+
+ Before calling the method, the caller will fill in data and datalen with
+ the payload blob parameters; quotalen will be filled in with the default
+ quota size from the key type and the rest will be cleared.
+
+ If a description can be proposed from the payload contents, that should be
+ attached as a string to the description field. This will be used for the
+ key description if the caller of add_key() passes NULL or "".
+
+ The method can attach anything it likes to type_data[] and payload. These
+ are merely passed along to the instantiate() or update() operations.
+
+ The method should return 0 if success ful or a negative error code
+ otherwise.
+
+
+ (*) void (*free_preparse)(struct key_preparsed_payload *prep);
+
+ This method is only required if the preparse() method is provided,
+ otherwise it is unused. It cleans up anything attached to the
+ description, type_data and payload fields of the key_preparsed_payload
+ struct as filled in by the preparse() method.
+
+
+ (*) int (*instantiate)(struct key *key, struct key_preparsed_payload *prep);

This method is called to attach a payload to a key during construction.
The payload attached need not bear any relation to the data passed to this
function.

+ The prep->data and prep->datalen fields will define the original payload
+ blob. If preparse() was supplied then other fields may be filled in also.
+
If the amount of data attached to the key differs from the size in
keytype->def_datalen, then key_payload_reserve() should be called.

@@ -1135,6 +1180,9 @@ The structure has a number of fields, some of which are mandatory:
If this type of key can be updated, then this method should be provided.
It is called to update a key's payload from the blob of data provided.

+ The prep->data and prep->datalen fields will define the original payload
+ blob. If preparse() was supplied then other fields may be filled in also.
+
key_payload_reserve() should be called if the data length might change
before any changes are actually made. Note that if this succeeds, the type
is committed to changing the key because it's already been altered, so all
diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
index e622863..086f381 100644
--- a/fs/cifs/cifs_spnego.c
+++ b/fs/cifs/cifs_spnego.c
@@ -31,18 +31,18 @@

/* create a new cifs key */
static int
-cifs_spnego_key_instantiate(struct key *key, const void *data, size_t datalen)
+cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
{
char *payload;
int ret;

ret = -ENOMEM;
- payload = kmalloc(datalen, GFP_KERNEL);
+ payload = kmalloc(prep->datalen, GFP_KERNEL);
if (!payload)
goto error;

/* attach the data */
- memcpy(payload, data, datalen);
+ memcpy(payload, prep->data, prep->datalen);
key->payload.data = payload;
ret = 0;

diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
index 05f4dc2..f3c60e2 100644
--- a/fs/cifs/cifsacl.c
+++ b/fs/cifs/cifsacl.c
@@ -167,17 +167,17 @@ static struct shrinker cifs_shrinker = {
};

static int
-cifs_idmap_key_instantiate(struct key *key, const void *data, size_t datalen)
+cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
{
char *payload;

- payload = kmalloc(datalen, GFP_KERNEL);
+ payload = kmalloc(prep->datalen, GFP_KERNEL);
if (!payload)
return -ENOMEM;

- memcpy(payload, data, datalen);
+ memcpy(payload, prep->data, prep->datalen);
key->payload.data = payload;
- key->datalen = datalen;
+ key->datalen = prep->datalen;
return 0;
}

diff --git a/include/keys/user-type.h b/include/keys/user-type.h
index bc9ec1d..5e452c8 100644
--- a/include/keys/user-type.h
+++ b/include/keys/user-type.h
@@ -35,8 +35,10 @@ struct user_key_payload {
extern struct key_type key_type_user;
extern struct key_type key_type_logon;

-extern int user_instantiate(struct key *key, const void *data, size_t datalen);
-extern int user_update(struct key *key, const void *data, size_t datalen);
+struct key_preparsed_payload;
+
+extern int user_instantiate(struct key *key, struct key_preparsed_payload *prep);
+extern int user_update(struct key *key, struct key_preparsed_payload *prep);
extern int user_match(const struct key *key, const void *criterion);
extern void user_revoke(struct key *key);
extern void user_destroy(struct key *key);
diff --git a/include/linux/key-type.h b/include/linux/key-type.h
index f0c651c..518a53a 100644
--- a/include/linux/key-type.h
+++ b/include/linux/key-type.h
@@ -26,6 +26,27 @@ struct key_construction {
struct key *authkey;/* authorisation for key being constructed */
};

+/*
+ * Pre-parsed payload, used by key add, update and instantiate.
+ *
+ * This struct will be cleared and data and datalen will be set with the data
+ * and length parameters from the caller and quotalen will be set from
+ * def_datalen from the key type. Then if the preparse() op is provided by the
+ * key type, that will be called. Then the struct will be passed to the
+ * instantiate() or the update() op.
+ *
+ * If the preparse() op is given, the free_preparse() op will be called to
+ * clear the contents.
+ */
+struct key_preparsed_payload {
+ char *description; /* Proposed key description (or NULL) */
+ void *type_data[2]; /* Private key-type data */
+ void *payload; /* Proposed payload */
+ const void *data; /* Raw data */
+ size_t datalen; /* Raw datalen */
+ size_t quotalen; /* Quota length for proposed payload */
+};
+
typedef int (*request_key_actor_t)(struct key_construction *key,
const char *op, void *aux);

@@ -45,18 +66,28 @@ struct key_type {
/* vet a description */
int (*vet_description)(const char *description);

+ /* Preparse the data blob from userspace that is to be the payload,
+ * generating a proposed description and payload that will be handed to
+ * the instantiate() and update() ops.
+ */
+ int (*preparse)(struct key_preparsed_payload *prep);
+
+ /* Free a preparse data structure.
+ */
+ void (*free_preparse)(struct key_preparsed_payload *prep);
+
/* instantiate a key of this type
* - this method should call key_payload_reserve() to determine if the
* user's quota will hold the payload
*/
- int (*instantiate)(struct key *key, const void *data, size_t datalen);
+ int (*instantiate)(struct key *key, struct key_preparsed_payload *prep);

/* update a key of this type (optional)
* - this method should call key_payload_reserve() to recalculate the
* quota consumption
* - the key must be locked against read when modifying
*/
- int (*update)(struct key *key, const void *data, size_t datalen);
+ int (*update)(struct key *key, struct key_preparsed_payload *prep);

/* match a key against a description */
int (*match)(const struct key *key, const void *desc);
diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c
index 9da7fdd..af14cb4 100644
--- a/net/ceph/crypto.c
+++ b/net/ceph/crypto.c
@@ -423,14 +423,15 @@ int ceph_encrypt2(struct ceph_crypto_key *secret, void *dst, size_t *dst_len,
}
}

-int ceph_key_instantiate(struct key *key, const void *data, size_t datalen)
+int ceph_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
{
struct ceph_crypto_key *ckey;
+ size_t datalen = prep->datalen;
int ret;
void *p;

ret = -EINVAL;
- if (datalen <= 0 || datalen > 32767 || !data)
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
goto err;

ret = key_payload_reserve(key, datalen);
@@ -443,8 +444,8 @@ int ceph_key_instantiate(struct key *key, const void *data, size_t datalen)
goto err;

/* TODO ceph_crypto_key_decode should really take const input */
- p = (void *)data;
- ret = ceph_crypto_key_decode(ckey, &p, (char*)data+datalen);
+ p = (void *)prep->data;
+ ret = ceph_crypto_key_decode(ckey, &p, (char*)prep->data+datalen);
if (ret < 0)
goto err_ckey;

diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c
index d9507dd..859ab8b 100644
--- a/net/dns_resolver/dns_key.c
+++ b/net/dns_resolver/dns_key.c
@@ -59,13 +59,13 @@ const struct cred *dns_resolver_cache;
* "ip1,ip2,...#foo=bar"
*/
static int
-dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen)
+dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep)
{
struct user_key_payload *upayload;
unsigned long derrno;
int ret;
- size_t result_len = 0;
- const char *data = _data, *end, *opt;
+ size_t datalen = prep->datalen, result_len = 0;
+ const char *data = prep->data, *end, *opt;

kenter("%%%d,%s,'%*.*s',%zu",
key->serial, key->description,
diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c
index 8b1f9f4..106c5a6 100644
--- a/net/rxrpc/ar-key.c
+++ b/net/rxrpc/ar-key.c
@@ -26,8 +26,8 @@
#include "ar-internal.h"

static int rxrpc_vet_description_s(const char *);
-static int rxrpc_instantiate(struct key *, const void *, size_t);
-static int rxrpc_instantiate_s(struct key *, const void *, size_t);
+static int rxrpc_instantiate(struct key *, struct key_preparsed_payload *);
+static int rxrpc_instantiate_s(struct key *, struct key_preparsed_payload *);
static void rxrpc_destroy(struct key *);
static void rxrpc_destroy_s(struct key *);
static void rxrpc_describe(const struct key *, struct seq_file *);
@@ -678,7 +678,7 @@ error:
*
* if no data is provided, then a no-security key is made
*/
-static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen)
+static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep)
{
const struct rxrpc_key_data_v1 *v1;
struct rxrpc_key_token *token, **pp;
@@ -686,26 +686,26 @@ static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen)
u32 kver;
int ret;

- _enter("{%x},,%zu", key_serial(key), datalen);
+ _enter("{%x},,%zu", key_serial(key), prep->datalen);

/* handle a no-security key */
- if (!data && datalen == 0)
+ if (!prep->data && prep->datalen == 0)
return 0;

/* determine if the XDR payload format is being used */
- if (datalen > 7 * 4) {
- ret = rxrpc_instantiate_xdr(key, data, datalen);
+ if (prep->datalen > 7 * 4) {
+ ret = rxrpc_instantiate_xdr(key, prep->data, prep->datalen);
if (ret != -EPROTO)
return ret;
}

/* get the key interface version number */
ret = -EINVAL;
- if (datalen <= 4 || !data)
+ if (prep->datalen <= 4 || !prep->data)
goto error;
- memcpy(&kver, data, sizeof(kver));
- data += sizeof(kver);
- datalen -= sizeof(kver);
+ memcpy(&kver, prep->data, sizeof(kver));
+ prep->data += sizeof(kver);
+ prep->datalen -= sizeof(kver);

_debug("KEY I/F VERSION: %u", kver);

@@ -715,11 +715,11 @@ static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen)

/* deal with a version 1 key */
ret = -EINVAL;
- if (datalen < sizeof(*v1))
+ if (prep->datalen < sizeof(*v1))
goto error;

- v1 = data;
- if (datalen != sizeof(*v1) + v1->ticket_length)
+ v1 = prep->data;
+ if (prep->datalen != sizeof(*v1) + v1->ticket_length)
goto error;

_debug("SCIX: %u", v1->security_index);
@@ -784,17 +784,17 @@ error:
* instantiate a server secret key
* data should be a pointer to the 8-byte secret key
*/
-static int rxrpc_instantiate_s(struct key *key, const void *data,
- size_t datalen)
+static int rxrpc_instantiate_s(struct key *key,
+ struct key_preparsed_payload *prep)
{
struct crypto_blkcipher *ci;

- _enter("{%x},,%zu", key_serial(key), datalen);
+ _enter("{%x},,%zu", key_serial(key), prep->datalen);

- if (datalen != 8)
+ if (prep->datalen != 8)
return -EINVAL;

- memcpy(&key->type_data, data, 8);
+ memcpy(&key->type_data, prep->data, 8);

ci = crypto_alloc_blkcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(ci)) {
@@ -802,7 +802,7 @@ static int rxrpc_instantiate_s(struct key *key, const void *data,
return PTR_ERR(ci);
}

- if (crypto_blkcipher_setkey(ci, data, 8) < 0)
+ if (crypto_blkcipher_setkey(ci, prep->data, 8) < 0)
BUG();

key->payload.data = ci;
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 2d1bb8a..9e1e005 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -773,8 +773,8 @@ static int encrypted_init(struct encrypted_key_payload *epayload,
*
* On success, return 0. Otherwise return errno.
*/
-static int encrypted_instantiate(struct key *key, const void *data,
- size_t datalen)
+static int encrypted_instantiate(struct key *key,
+ struct key_preparsed_payload *prep)
{
struct encrypted_key_payload *epayload = NULL;
char *datablob = NULL;
@@ -782,16 +782,17 @@ static int encrypted_instantiate(struct key *key, const void *data,
char *master_desc = NULL;
char *decrypted_datalen = NULL;
char *hex_encoded_iv = NULL;
+ size_t datalen = prep->datalen;
int ret;

- if (datalen <= 0 || datalen > 32767 || !data)
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
return -EINVAL;

datablob = kmalloc(datalen + 1, GFP_KERNEL);
if (!datablob)
return -ENOMEM;
datablob[datalen] = 0;
- memcpy(datablob, data, datalen);
+ memcpy(datablob, prep->data, datalen);
ret = datablob_parse(datablob, &format, &master_desc,
&decrypted_datalen, &hex_encoded_iv);
if (ret < 0)
@@ -834,16 +835,17 @@ static void encrypted_rcu_free(struct rcu_head *rcu)
*
* On success, return 0. Otherwise return errno.
*/
-static int encrypted_update(struct key *key, const void *data, size_t datalen)
+static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)
{
struct encrypted_key_payload *epayload = key->payload.data;
struct encrypted_key_payload *new_epayload;
char *buf;
char *new_master_desc = NULL;
const char *format = NULL;
+ size_t datalen = prep->datalen;
int ret = 0;

- if (datalen <= 0 || datalen > 32767 || !data)
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
return -EINVAL;

buf = kmalloc(datalen + 1, GFP_KERNEL);
@@ -851,7 +853,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen)
return -ENOMEM;

buf[datalen] = 0;
- memcpy(buf, data, datalen);
+ memcpy(buf, prep->data, datalen);
ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL);
if (ret < 0)
goto out;
diff --git a/security/keys/key.c b/security/keys/key.c
index 50d96d4..1d039af 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -412,8 +412,7 @@ EXPORT_SYMBOL(key_payload_reserve);
* key_construction_mutex.
*/
static int __key_instantiate_and_link(struct key *key,
- const void *data,
- size_t datalen,
+ struct key_preparsed_payload *prep,
struct key *keyring,
struct key *authkey,
unsigned long *_prealloc)
@@ -431,7 +430,7 @@ static int __key_instantiate_and_link(struct key *key,
/* can't instantiate twice */
if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
/* instantiate the key */
- ret = key->type->instantiate(key, data, datalen);
+ ret = key->type->instantiate(key, prep);

if (ret == 0) {
/* mark the key as being instantiated */
@@ -482,22 +481,37 @@ int key_instantiate_and_link(struct key *key,
struct key *keyring,
struct key *authkey)
{
+ struct key_preparsed_payload prep;
unsigned long prealloc;
int ret;

+ memset(&prep, 0, sizeof(prep));
+ prep.data = data;
+ prep.datalen = datalen;
+ prep.quotalen = key->type->def_datalen;
+ if (key->type->preparse) {
+ ret = key->type->preparse(&prep);
+ if (ret < 0)
+ goto error;
+ }
+
if (keyring) {
ret = __key_link_begin(keyring, key->type, key->description,
&prealloc);
if (ret < 0)
- return ret;
+ goto error_free_preparse;
}

- ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey,
+ ret = __key_instantiate_and_link(key, &prep, keyring, authkey,
&prealloc);

if (keyring)
__key_link_end(keyring, key->type, prealloc);

+error_free_preparse:
+ if (key->type->preparse)
+ key->type->free_preparse(&prep);
+error:
return ret;
}

@@ -706,7 +720,7 @@ void key_type_put(struct key_type *ktype)
* if we get an error.
*/
static inline key_ref_t __key_update(key_ref_t key_ref,
- const void *payload, size_t plen)
+ struct key_preparsed_payload *prep)
{
struct key *key = key_ref_to_ptr(key_ref);
int ret;
@@ -722,7 +736,7 @@ static inline key_ref_t __key_update(key_ref_t key_ref,

down_write(&key->sem);

- ret = key->type->update(key, payload, plen);
+ ret = key->type->update(key, prep);
if (ret == 0)
/* updating a negative key instantiates it */
clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
@@ -774,6 +788,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
unsigned long flags)
{
unsigned long prealloc;
+ struct key_preparsed_payload prep;
const struct cred *cred = current_cred();
struct key_type *ktype;
struct key *keyring, *key = NULL;
@@ -789,8 +804,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
}

key_ref = ERR_PTR(-EINVAL);
- if (!ktype->match || !ktype->instantiate)
- goto error_2;
+ if (!ktype->match || !ktype->instantiate ||
+ (!description && !ktype->preparse))
+ goto error_put_type;

keyring = key_ref_to_ptr(keyring_ref);

@@ -798,18 +814,37 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,

key_ref = ERR_PTR(-ENOTDIR);
if (keyring->type != &key_type_keyring)
- goto error_2;
+ goto error_put_type;
+
+ memset(&prep, 0, sizeof(prep));
+ prep.data = payload;
+ prep.datalen = plen;
+ prep.quotalen = ktype->def_datalen;
+ if (ktype->preparse) {
+ ret = ktype->preparse(&prep);
+ if (ret < 0) {
+ key_ref = ERR_PTR(ret);
+ goto error_put_type;
+ }
+ if (!description)
+ description = prep.description;
+ key_ref = ERR_PTR(-EINVAL);
+ if (!description)
+ goto error_free_prep;
+ }

ret = __key_link_begin(keyring, ktype, description, &prealloc);
- if (ret < 0)
- goto error_2;
+ if (ret < 0) {
+ key_ref = ERR_PTR(ret);
+ goto error_free_prep;
+ }

/* if we're going to allocate a new key, we're going to have
* to modify the keyring */
ret = key_permission(keyring_ref, KEY_WRITE);
if (ret < 0) {
key_ref = ERR_PTR(ret);
- goto error_3;
+ goto error_link_end;
}

/* if it's possible to update this type of key, search for an existing
@@ -840,25 +875,27 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
perm, flags);
if (IS_ERR(key)) {
key_ref = ERR_CAST(key);
- goto error_3;
+ goto error_link_end;
}

/* instantiate it and link it into the target keyring */
- ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL,
- &prealloc);
+ ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &prealloc);
if (ret < 0) {
key_put(key);
key_ref = ERR_PTR(ret);
- goto error_3;
+ goto error_link_end;
}

key_ref = make_key_ref(key, is_key_possessed(keyring_ref));

- error_3:
+error_link_end:
__key_link_end(keyring, ktype, prealloc);
- error_2:
+error_free_prep:
+ if (ktype->preparse)
+ ktype->free_preparse(&prep);
+error_put_type:
key_type_put(ktype);
- error:
+error:
return key_ref;

found_matching_key:
@@ -866,10 +903,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
* - we can drop the locks first as we have the key pinned
*/
__key_link_end(keyring, ktype, prealloc);
- key_type_put(ktype);

- key_ref = __key_update(key_ref, payload, plen);
- goto error;
+ key_ref = __key_update(key_ref, &prep);
+ goto error_free_prep;
}
EXPORT_SYMBOL(key_create_or_update);

@@ -888,6 +924,7 @@ EXPORT_SYMBOL(key_create_or_update);
*/
int key_update(key_ref_t key_ref, const void *payload, size_t plen)
{
+ struct key_preparsed_payload prep;
struct key *key = key_ref_to_ptr(key_ref);
int ret;

@@ -900,18 +937,31 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)

/* attempt to update it if supported */
ret = -EOPNOTSUPP;
- if (key->type->update) {
- down_write(&key->sem);
-
- ret = key->type->update(key, payload, plen);
- if (ret == 0)
- /* updating a negative key instantiates it */
- clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
+ if (!key->type->update)
+ goto error;

- up_write(&key->sem);
+ memset(&prep, 0, sizeof(prep));
+ prep.data = payload;
+ prep.datalen = plen;
+ prep.quotalen = key->type->def_datalen;
+ if (key->type->preparse) {
+ ret = key->type->preparse(&prep);
+ if (ret < 0)
+ goto error;
}

- error:
+ down_write(&key->sem);
+
+ ret = key->type->update(key, &prep);
+ if (ret == 0)
+ /* updating a negative key instantiates it */
+ clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
+
+ up_write(&key->sem);
+
+ if (key->type->preparse)
+ key->type->free_preparse(&prep);
+error:
return ret;
}
EXPORT_SYMBOL(key_update);
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 3364fbf..505d40b 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -46,6 +46,9 @@ static int key_get_type_from_user(char *type,
* Extract the description of a new key from userspace and either add it as a
* new key to the specified keyring or update a matching key in that keyring.
*
+ * If the description is NULL or an empty string, the key type is asked to
+ * generate one from the payload.
+ *
* The keyring must be writable so that we can attach the key to it.
*
* If successful, the new key's serial number is returned, otherwise an error
@@ -72,10 +75,17 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
if (ret < 0)
goto error;

- description = strndup_user(_description, PAGE_SIZE);
- if (IS_ERR(description)) {
- ret = PTR_ERR(description);
- goto error;
+ description = NULL;
+ if (_description) {
+ description = strndup_user(_description, PAGE_SIZE);
+ if (IS_ERR(description)) {
+ ret = PTR_ERR(description);
+ goto error;
+ }
+ if (!*description) {
+ kfree(description);
+ description = NULL;
+ }
}

/* pull the payload in if one was supplied */
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 81e7852..f04d8cf 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -66,7 +66,7 @@ static inline unsigned keyring_hash(const char *desc)
* operations.
*/
static int keyring_instantiate(struct key *keyring,
- const void *data, size_t datalen);
+ struct key_preparsed_payload *prep);
static int keyring_match(const struct key *keyring, const void *criterion);
static void keyring_revoke(struct key *keyring);
static void keyring_destroy(struct key *keyring);
@@ -121,12 +121,12 @@ static void keyring_publish_name(struct key *keyring)
* Returns 0 on success, -EINVAL if given any data.
*/
static int keyring_instantiate(struct key *keyring,
- const void *data, size_t datalen)
+ struct key_preparsed_payload *prep)
{
int ret;

ret = -EINVAL;
- if (datalen == 0) {
+ if (prep->datalen == 0) {
/* make the keyring available by name if it has one */
keyring_publish_name(keyring);
ret = 0;
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 60d4e3f..85730d5 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -19,7 +19,8 @@
#include <asm/uaccess.h>
#include "internal.h"

-static int request_key_auth_instantiate(struct key *, const void *, size_t);
+static int request_key_auth_instantiate(struct key *,
+ struct key_preparsed_payload *);
static void request_key_auth_describe(const struct key *, struct seq_file *);
static void request_key_auth_revoke(struct key *);
static void request_key_auth_destroy(struct key *);
@@ -42,10 +43,9 @@ struct key_type key_type_request_key_auth = {
* Instantiate a request-key authorisation key.
*/
static int request_key_auth_instantiate(struct key *key,
- const void *data,
- size_t datalen)
+ struct key_preparsed_payload *prep)
{
- key->payload.data = (struct request_key_auth *) data;
+ key->payload.data = (struct request_key_auth *)prep->data;
return 0;
}

diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index 2d5d041..42036c7 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -927,22 +927,23 @@ static struct trusted_key_payload *trusted_payload_alloc(struct key *key)
*
* On success, return 0. Otherwise return errno.
*/
-static int trusted_instantiate(struct key *key, const void *data,
- size_t datalen)
+static int trusted_instantiate(struct key *key,
+ struct key_preparsed_payload *prep)
{
struct trusted_key_payload *payload = NULL;
struct trusted_key_options *options = NULL;
+ size_t datalen = prep->datalen;
char *datablob;
int ret = 0;
int key_cmd;

- if (datalen <= 0 || datalen > 32767 || !data)
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
return -EINVAL;

datablob = kmalloc(datalen + 1, GFP_KERNEL);
if (!datablob)
return -ENOMEM;
- memcpy(datablob, data, datalen);
+ memcpy(datablob, prep->data, datalen);
datablob[datalen] = '\0';

options = trusted_options_alloc();
@@ -1011,17 +1012,18 @@ static void trusted_rcu_free(struct rcu_head *rcu)
/*
* trusted_update - reseal an existing key with new PCR values
*/
-static int trusted_update(struct key *key, const void *data, size_t datalen)
+static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
{
struct trusted_key_payload *p = key->payload.data;
struct trusted_key_payload *new_p;
struct trusted_key_options *new_o;
+ size_t datalen = prep->datalen;
char *datablob;
int ret = 0;

if (!p->migratable)
return -EPERM;
- if (datalen <= 0 || datalen > 32767 || !data)
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
return -EINVAL;

datablob = kmalloc(datalen + 1, GFP_KERNEL);
@@ -1038,7 +1040,7 @@ static int trusted_update(struct key *key, const void *data, size_t datalen)
goto out;
}

- memcpy(datablob, data, datalen);
+ memcpy(datablob, prep->data, datalen);
datablob[datalen] = '\0';
ret = datablob_parse(datablob, new_p, new_o);
if (ret != Opt_update) {
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index c7660a2..55dc889 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -58,13 +58,14 @@ EXPORT_SYMBOL_GPL(key_type_logon);
/*
* instantiate a user defined key
*/
-int user_instantiate(struct key *key, const void *data, size_t datalen)
+int user_instantiate(struct key *key, struct key_preparsed_payload *prep)
{
struct user_key_payload *upayload;
+ size_t datalen = prep->datalen;
int ret;

ret = -EINVAL;
- if (datalen <= 0 || datalen > 32767 || !data)
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
goto error;

ret = key_payload_reserve(key, datalen);
@@ -78,7 +79,7 @@ int user_instantiate(struct key *key, const void *data, size_t datalen)

/* attach the data */
upayload->datalen = datalen;
- memcpy(upayload->data, data, datalen);
+ memcpy(upayload->data, prep->data, datalen);
rcu_assign_keypointer(key, upayload);
ret = 0;

@@ -92,13 +93,14 @@ EXPORT_SYMBOL_GPL(user_instantiate);
* update a user defined key
* - the key's semaphore is write-locked
*/
-int user_update(struct key *key, const void *data, size_t datalen)
+int user_update(struct key *key, struct key_preparsed_payload *prep)
{
struct user_key_payload *upayload, *zap;
+ size_t datalen = prep->datalen;
int ret;

ret = -EINVAL;
- if (datalen <= 0 || datalen > 32767 || !data)
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
goto error;

/* construct a replacement payload */
@@ -108,7 +110,7 @@ int user_update(struct key *key, const void *data, size_t datalen)
goto error;

upayload->datalen = datalen;
- memcpy(upayload->data, data, datalen);
+ memcpy(upayload->data, prep->data, datalen);

/* check the quota and attach the new data */
zap = upayload;

2012-09-13 23:48:57

by David Howells

[permalink] [raw]
Subject: [PATCH 06/16] KEYS: Asymmetric public-key algorithm crypto key subtype

Add a subtype for supporting asymmetric public-key encryption algorithms such
as DSA (FIPS-186) and RSA (PKCS#1 / RFC1337).

Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/Kconfig | 8 +++
crypto/asymmetric_keys/Makefile | 2 +
crypto/asymmetric_keys/public_key.c | 108 +++++++++++++++++++++++++++++++++++
crypto/asymmetric_keys/public_key.h | 28 +++++++++
include/crypto/public_key.h | 109 +++++++++++++++++++++++++++++++++++
5 files changed, 255 insertions(+)
create mode 100644 crypto/asymmetric_keys/public_key.c
create mode 100644 crypto/asymmetric_keys/public_key.h
create mode 100644 include/crypto/public_key.h


diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index cad29b3..bbfccaa 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -9,5 +9,13 @@ menuconfig ASYMMETRIC_KEY_TYPE

if ASYMMETRIC_KEY_TYPE

+config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ tristate "Asymmetric public-key crypto algorithm subtype"
+ select MPILIB
+ 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/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index b725bcc..5ed46ee 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -5,3 +5,5 @@
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/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
new file mode 100644
index 0000000..cb2e291
--- /dev/null
+++ b/crypto/asymmetric_keys/public_key.c
@@ -0,0 +1,108 @@
+/* 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 ([email protected])
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "PKEY: "fmt
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <keys/asymmetric-subtype.h>
+#include "public_key.h"
+
+MODULE_LICENSE("GPL");
+
+const char *const pkey_algo[PKEY_ALGO__LAST] = {
+ [PKEY_ALGO_DSA] = "DSA",
+ [PKEY_ALGO_RSA] = "RSA",
+};
+EXPORT_SYMBOL_GPL(pkey_algo);
+
+const char *const pkey_hash_algo[PKEY_HASH__LAST] = {
+ [PKEY_HASH_MD4] = "md4",
+ [PKEY_HASH_MD5] = "md5",
+ [PKEY_HASH_SHA1] = "sha1",
+ [PKEY_HASH_RIPE_MD_160] = "rmd160",
+ [PKEY_HASH_SHA256] = "sha256",
+ [PKEY_HASH_SHA384] = "sha384",
+ [PKEY_HASH_SHA512] = "sha512",
+ [PKEY_HASH_SHA224] = "sha224",
+};
+EXPORT_SYMBOL_GPL(pkey_hash_algo);
+
+const char *const pkey_id_type[PKEY_ID_TYPE__LAST] = {
+ [PKEY_ID_PGP] = "PGP",
+ [PKEY_ID_X509] = "X509",
+};
+EXPORT_SYMBOL_GPL(pkey_id_type);
+
+/*
+ * 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;
+
+ if (key)
+ seq_printf(m, "%s.%s",
+ pkey_id_type[key->id_type], key->algo->name);
+}
+
+/*
+ * Destroy a public key algorithm key.
+ */
+void public_key_destroy(void *payload)
+{
+ struct public_key *key = payload;
+ int i;
+
+ if (key) {
+ for (i = 0; i < ARRAY_SIZE(key->mpi); i++)
+ mpi_free(key->mpi[i]);
+ kfree(key);
+ }
+}
+EXPORT_SYMBOL_GPL(public_key_destroy);
+
+/*
+ * Verify a signature using a public key.
+ */
+static int public_key_verify_signature(const struct key *key,
+ const struct public_key_signature *sig)
+{
+ const struct public_key *pk = key->payload.data;
+
+ if (!pk->algo->verify_signature)
+ return -ENOTSUPP;
+
+ if (sig->nr_mpi != pk->algo->n_sig_mpi) {
+ pr_debug("Signature has %u MPI not %u\n",
+ sig->nr_mpi, pk->algo->n_sig_mpi);
+ return -EINVAL;
+ }
+
+ return pk->algo->verify_signature(pk, sig);
+}
+
+/*
+ * Public key algorithm asymmetric key subtype
+ */
+struct asymmetric_key_subtype public_key_subtype = {
+ .owner = THIS_MODULE,
+ .name = "public_key",
+ .describe = public_key_describe,
+ .destroy = public_key_destroy,
+ .verify_signature = public_key_verify_signature,
+};
+EXPORT_SYMBOL_GPL(public_key_subtype);
diff --git a/crypto/asymmetric_keys/public_key.h b/crypto/asymmetric_keys/public_key.h
new file mode 100644
index 0000000..1f86aad
--- /dev/null
+++ b/crypto/asymmetric_keys/public_key.h
@@ -0,0 +1,28 @@
+/* Public key algorithm internals
+ *
+ * See Documentation/crypto/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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.
+ */
+
+#include <crypto/public_key.h>
+
+extern struct asymmetric_key_subtype public_key_subtype;
+
+/*
+ * Public key algorithm definition.
+ */
+struct public_key_algorithm {
+ const char *name;
+ u8 n_pub_mpi; /* Number of MPIs in public key */
+ u8 n_sec_mpi; /* Number of MPIs in secret key */
+ u8 n_sig_mpi; /* Number of MPIs in a signature */
+ int (*verify_signature)(const struct public_key *key,
+ const struct public_key_signature *sig);
+};
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
new file mode 100644
index 0000000..65d9879
--- /dev/null
+++ b/include/crypto/public_key.h
@@ -0,0 +1,109 @@
+/* Asymmetric public-key algorithm definitions
+ *
+ * See Documentation/crypto/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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_PUBLIC_KEY_H
+#define _LINUX_PUBLIC_KEY_H
+
+#include <linux/mpi.h>
+
+enum pkey_algo {
+ PKEY_ALGO_DSA,
+ PKEY_ALGO_RSA,
+ PKEY_ALGO__LAST
+};
+
+extern const char *const pkey_algo[PKEY_ALGO__LAST];
+
+enum pkey_hash_algo {
+ PKEY_HASH_MD4,
+ PKEY_HASH_MD5,
+ PKEY_HASH_SHA1,
+ PKEY_HASH_RIPE_MD_160,
+ PKEY_HASH_SHA256,
+ PKEY_HASH_SHA384,
+ PKEY_HASH_SHA512,
+ PKEY_HASH_SHA224,
+ PKEY_HASH__LAST
+};
+
+extern const char *const pkey_hash_algo[PKEY_HASH__LAST];
+
+enum pkey_id_type {
+ PKEY_ID_PGP, /* OpenPGP generated key ID */
+ PKEY_ID_X509, /* X.509 arbitrary subjectKeyIdentifier */
+ PKEY_ID_TYPE__LAST
+};
+
+extern const char *const pkey_id_type[PKEY_ID_TYPE__LAST];
+
+/*
+ * 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 {
+ const struct public_key_algorithm *algo;
+ u8 capabilities;
+#define PKEY_CAN_ENCRYPT 0x01
+#define PKEY_CAN_DECRYPT 0x02
+#define PKEY_CAN_ENCDEC (PKEY_CAN_ENCRYPT | PKEY_CAN_DECRYPT)
+#define PKEY_CAN_SIGN 0x04
+#define PKEY_CAN_VERIFY 0x08
+#define PKEY_CAN_SIGVER (PKEY_CAN_SIGN | PKEY_CAN_VERIFY)
+ enum pkey_id_type id_type : 8;
+ union {
+ MPI mpi[5];
+ struct {
+ MPI p; /* DSA prime */
+ MPI q; /* DSA group order */
+ MPI g; /* DSA group generator */
+ MPI y; /* DSA public-key value = g^x mod p */
+ MPI x; /* DSA secret exponent (if present) */
+ } dsa;
+ struct {
+ MPI n; /* RSA public modulus */
+ MPI e; /* RSA public encryption exponent */
+ MPI d; /* RSA secret encryption exponent (if present) */
+ MPI p; /* RSA secret prime (if present) */
+ MPI q; /* RSA secret prime (if present) */
+ } rsa;
+ };
+
+ u8 key_id[8]; /* ID of this key pair */
+ u8 key_id_size; /* Number of bytes in key_id */
+};
+
+extern void public_key_destroy(void *payload);
+
+/*
+ * Public key cryptography signature data
+ */
+struct public_key_signature {
+ u8 *digest;
+ u8 digest_size; /* Number of bytes in digest */
+ u8 nr_mpi; /* Occupancy of mpi[] */
+ enum pkey_hash_algo pkey_hash_algo : 8;
+ union {
+ MPI mpi[2];
+ struct {
+ MPI s; /* m^d mod n */
+ } rsa;
+ struct {
+ MPI r;
+ MPI s;
+ } dsa;
+ };
+};
+
+#endif /* _LINUX_PUBLIC_KEY_H */

2012-09-13 23:49:15

by David Howells

[permalink] [raw]
Subject: [PATCH 08/16] MPILIB: Reinstate mpi_cmp[_ui]() and export for RSA signature verification

Reinstate and export mpi_cmp() and mpi_cmp_ui() from the MPI library for use by
RSA signature verification as per RFC3447 section 5.2.2 step 1.

Signed-off-by: David Howells <[email protected]>
---

lib/mpi/Makefile | 1 +
lib/mpi/mpi-cmp.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 71 insertions(+)
create mode 100644 lib/mpi/mpi-cmp.c


diff --git a/lib/mpi/Makefile b/lib/mpi/Makefile
index 45ca90a..019a68c 100644
--- a/lib/mpi/Makefile
+++ b/lib/mpi/Makefile
@@ -14,6 +14,7 @@ mpi-y = \
generic_mpih-add1.o \
mpicoder.o \
mpi-bit.o \
+ mpi-cmp.o \
mpih-cmp.o \
mpih-div.o \
mpih-mul.o \
diff --git a/lib/mpi/mpi-cmp.c b/lib/mpi/mpi-cmp.c
new file mode 100644
index 0000000..1871e7b
--- /dev/null
+++ b/lib/mpi/mpi-cmp.c
@@ -0,0 +1,70 @@
+/* mpi-cmp.c - MPI functions
+ * Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "mpi-internal.h"
+
+int mpi_cmp_ui(MPI u, unsigned long v)
+{
+ mpi_limb_t limb = v;
+
+ mpi_normalize(u);
+ if (!u->nlimbs && !limb)
+ return 0;
+ if (u->sign)
+ return -1;
+ if (u->nlimbs > 1)
+ return 1;
+
+ if (u->d[0] == limb)
+ return 0;
+ else if (u->d[0] > limb)
+ return 1;
+ else
+ return -1;
+}
+EXPORT_SYMBOL_GPL(mpi_cmp_ui);
+
+int mpi_cmp(MPI u, MPI v)
+{
+ mpi_size_t usize, vsize;
+ int cmp;
+
+ mpi_normalize(u);
+ mpi_normalize(v);
+ usize = u->nlimbs;
+ vsize = v->nlimbs;
+ if (!u->sign && v->sign)
+ return 1;
+ if (u->sign && !v->sign)
+ return -1;
+ if (usize != vsize && !u->sign && !v->sign)
+ return usize - vsize;
+ if (usize != vsize && u->sign && v->sign)
+ return vsize + usize;
+ if (!usize)
+ return 0;
+ cmp = mpihelp_cmp(u->d, v->d, usize);
+ if (!cmp)
+ return 0;
+ if ((cmp < 0 ? 1 : 0) == (u->sign ? 1 : 0))
+ return 1;
+ return -1;
+}
+EXPORT_SYMBOL_GPL(mpi_cmp);

2012-09-13 23:49:22

by David Howells

[permalink] [raw]
Subject: [PATCH 09/16] RSA: Implement signature verification algorithm [PKCS#1 / RFC3447]

Implement RSA public key cryptography [PKCS#1 / RFC3447]. At this time, only
the signature verification algorithm is supported. This uses the asymmetric
public key subtype to hold its key data.

Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/Kconfig | 7 +
crypto/asymmetric_keys/Makefile | 1
crypto/asymmetric_keys/public_key.h | 2
crypto/asymmetric_keys/rsa.c | 269 +++++++++++++++++++++++++++++++++++
4 files changed, 279 insertions(+)
create mode 100644 crypto/asymmetric_keys/rsa.c


diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index bbfccaa..561759d 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -18,4 +18,11 @@ 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 PUBLIC_KEY_ALGO_RSA
+ tristate "RSA public-key algorithm"
+ depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ select MPILIB_EXTRA
+ help
+ This option enables support for the RSA algorithm (PKCS#1, RFC3447).
+
endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 8dcdf0c..7c92691 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
asymmetric_keys-y := asymmetric_type.o signature.o

obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
+obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o
diff --git a/crypto/asymmetric_keys/public_key.h b/crypto/asymmetric_keys/public_key.h
index 1f86aad..5e5e356 100644
--- a/crypto/asymmetric_keys/public_key.h
+++ b/crypto/asymmetric_keys/public_key.h
@@ -26,3 +26,5 @@ struct public_key_algorithm {
int (*verify_signature)(const struct public_key *key,
const struct public_key_signature *sig);
};
+
+extern const struct public_key_algorithm RSA_public_key_algorithm;
diff --git a/crypto/asymmetric_keys/rsa.c b/crypto/asymmetric_keys/rsa.c
new file mode 100644
index 0000000..9b31ee2
--- /dev/null
+++ b/crypto/asymmetric_keys/rsa.c
@@ -0,0 +1,269 @@
+/* RSA asymmetric public-key algorithm [RFC3447]
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "RSA: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include "public_key.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RSA Public Key Algorithm");
+
+#define kenter(FMT, ...) \
+ pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+ pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+
+/*
+ * Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2].
+ */
+static const u8 RSA_digest_info_MD5[] = {
+ 0x30, 0x20, 0x30, 0x0C, 0x06, 0x08,
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* OID */
+ 0x05, 0x00, 0x04, 0x10
+};
+
+static const u8 RSA_digest_info_SHA1[] = {
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
+ 0x2B, 0x0E, 0x03, 0x02, 0x1A,
+ 0x05, 0x00, 0x04, 0x14
+};
+
+static const u8 RSA_digest_info_RIPE_MD_160[] = {
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
+ 0x2B, 0x24, 0x03, 0x02, 0x01,
+ 0x05, 0x00, 0x04, 0x14
+};
+
+static const u8 RSA_digest_info_SHA224[] = {
+ 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04,
+ 0x05, 0x00, 0x04, 0x1C
+};
+
+static const u8 RSA_digest_info_SHA256[] = {
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
+ 0x05, 0x00, 0x04, 0x20
+};
+
+static const u8 RSA_digest_info_SHA384[] = {
+ 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
+ 0x05, 0x00, 0x04, 0x30
+};
+
+static const u8 RSA_digest_info_SHA512[] = {
+ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
+ 0x05, 0x00, 0x04, 0x40
+};
+
+static const struct {
+ const u8 *data;
+ size_t size;
+} RSA_ASN1_templates[PKEY_HASH__LAST] = {
+#define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) }
+ [PKEY_HASH_MD5] = _(MD5),
+ [PKEY_HASH_SHA1] = _(SHA1),
+ [PKEY_HASH_RIPE_MD_160] = _(RIPE_MD_160),
+ [PKEY_HASH_SHA256] = _(SHA256),
+ [PKEY_HASH_SHA384] = _(SHA384),
+ [PKEY_HASH_SHA512] = _(SHA512),
+ [PKEY_HASH_SHA224] = _(SHA224),
+#undef _
+};
+
+/*
+ * RSAVP1() function [RFC3447 sec 5.2.2]
+ */
+static int RSAVP1(const struct public_key *key, MPI s, MPI *_m)
+{
+ MPI m;
+ int ret;
+
+ /* (1) Validate 0 <= s < n */
+ if (mpi_cmp_ui(s, 0) < 0) {
+ kleave(" = -EBADMSG [s < 0]");
+ return -EBADMSG;
+ }
+ if (mpi_cmp(s, key->rsa.n) >= 0) {
+ kleave(" = -EBADMSG [s >= n]");
+ return -EBADMSG;
+ }
+
+ m = mpi_alloc(0);
+ if (!m)
+ return -ENOMEM;
+
+ /* (2) m = s^e mod n */
+ ret = mpi_powm(m, s, key->rsa.e, key->rsa.n);
+ if (ret < 0) {
+ mpi_free(m);
+ return ret;
+ }
+
+ *_m = m;
+ return 0;
+}
+
+/*
+ * Integer to Octet String conversion [RFC3447 sec 4.1]
+ */
+static int RSA_I2OSP(MPI x, size_t xLen, u8 **_X)
+{
+ unsigned X_size, x_size;
+ int X_sign;
+ u8 *X;
+
+ /* Make sure the string is the right length. The number should begin
+ * with { 0x00, 0x01, ... } so we have to account for 15 leading zero
+ * bits not being reported by MPI.
+ */
+ x_size = mpi_get_nbits(x);
+ pr_devel("size(x)=%u xLen*8=%zu\n", x_size, xLen * 8);
+ if (x_size != xLen * 8 - 15)
+ return -ERANGE;
+
+ X = mpi_get_buffer(x, &X_size, &X_sign);
+ if (!X)
+ return -ENOMEM;
+ if (X_sign < 0) {
+ kfree(X);
+ return -EBADMSG;
+ }
+ if (X_size != xLen - 1) {
+ kfree(X);
+ return -EBADMSG;
+ }
+
+ *_X = X;
+ return 0;
+}
+
+/*
+ * Perform the RSA signature verification.
+ * @H: Value of hash of data and metadata
+ * @EM: The computed signature value
+ * @k: The size of EM (EM[0] is an invalid location but should hold 0x00)
+ * @hash_size: The size of H
+ * @asn1_template: The DigestInfo ASN.1 template
+ * @asn1_size: Size of asm1_template[]
+ */
+static int RSA_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size,
+ const u8 *asn1_template, size_t asn1_size)
+{
+ unsigned PS_end, T_offset, i;
+
+ kenter(",,%zu,%zu,%zu", k, hash_size, asn1_size);
+
+ if (k < 2 + 1 + asn1_size + hash_size)
+ return -EBADMSG;
+
+ /* Decode the EMSA-PKCS1-v1_5 */
+ if (EM[1] != 0x01) {
+ kleave(" = -EBADMSG [EM[1] == %02u]", EM[1]);
+ return -EBADMSG;
+ }
+
+ T_offset = k - (asn1_size + hash_size);
+ PS_end = T_offset - 1;
+ if (EM[PS_end] != 0x00) {
+ kleave(" = -EBADMSG [EM[T-1] == %02u]", EM[PS_end]);
+ return -EBADMSG;
+ }
+
+ for (i = 2; i < PS_end; i++) {
+ if (EM[i] != 0xff) {
+ kleave(" = -EBADMSG [EM[PS%x] == %02u]", i - 2, EM[i]);
+ return -EBADMSG;
+ }
+ }
+
+ if (memcmp(asn1_template, EM + T_offset, asn1_size) != 0) {
+ kleave(" = -EBADMSG [EM[T] ASN.1 mismatch]");
+ return -EBADMSG;
+ }
+
+ if (memcmp(H, EM + T_offset + asn1_size, hash_size) != 0) {
+ kleave(" = -EKEYREJECTED [EM[T] hash mismatch]");
+ return -EKEYREJECTED;
+ }
+
+ kleave(" = 0");
+ return 0;
+}
+
+/*
+ * Perform the verification step [RFC3447 sec 8.2.2].
+ */
+static int RSA_verify_signature(const struct public_key *key,
+ const struct public_key_signature *sig)
+{
+ size_t tsize;
+ int ret;
+
+ /* Variables as per RFC3447 sec 8.2.2 */
+ const u8 *H = sig->digest;
+ u8 *EM = NULL;
+ MPI m = NULL;
+ size_t k;
+
+ kenter("");
+
+ if (!RSA_ASN1_templates[sig->pkey_hash_algo].data)
+ return -ENOTSUPP;
+
+ /* (1) Check the signature size against the public key modulus size */
+ k = (mpi_get_nbits(key->rsa.n) + 7) / 8;
+
+ tsize = (mpi_get_nbits(sig->rsa.s) + 7) / 8;
+ pr_devel("step 1: k=%zu size(S)=%zu\n", k, tsize);
+ if (tsize != k) {
+ ret = -EBADMSG;
+ goto error;
+ }
+
+ /* (2b) Apply the RSAVP1 verification primitive to the public key */
+ ret = RSAVP1(key, sig->rsa.s, &m);
+ if (ret < 0)
+ goto error;
+
+ /* (2c) Convert the message representative (m) to an encoded message
+ * (EM) of length k octets.
+ *
+ * NOTE! The leading zero byte is suppressed by MPI, so we pass a
+ * pointer to the _preceding_ byte to RSA_verify()!
+ */
+ ret = RSA_I2OSP(m, k, &EM);
+ if (ret < 0)
+ goto error;
+
+ ret = RSA_verify(H, EM - 1, k, sig->digest_size,
+ RSA_ASN1_templates[sig->pkey_hash_algo].data,
+ RSA_ASN1_templates[sig->pkey_hash_algo].size);
+
+error:
+ kfree(EM);
+ mpi_free(m);
+ kleave(" = %d", ret);
+ return ret;
+}
+
+const struct public_key_algorithm RSA_public_key_algorithm = {
+ .name = "RSA",
+ .n_pub_mpi = 2,
+ .n_sec_mpi = 3,
+ .n_sig_mpi = 1,
+ .verify_signature = RSA_verify_signature,
+};
+EXPORT_SYMBOL_GPL(RSA_public_key_algorithm);


2012-09-13 23:49:51

by David Howells

[permalink] [raw]
Subject: [PATCH 11/16] X.509: Implement simple static OID registry

Implement a simple static OID registry that allows the mapping of an encoded
OID to an enum value for ease of use.

The OID registry index enum appears in the:

linux/oid_registry.h

header file. A script generates the registry from lines in the header file
that look like:

<sp*>OID_foo,<sp*>/*<sp*>1.2.3.4<sp*>*/

The actual OID is taken to be represented by the numbers with interpolated
dots in the comment.

All other lines in the header are ignored.

The registry is queries by calling:

OID look_up_oid(const void *data, size_t datasize);

This returns a number from the registry enum representing the OID if found or
OID__NR if not.

Signed-off-by: David Howells <[email protected]>
---

include/linux/oid_registry.h | 90 ++++++++++++++++++
lib/.gitignore | 2
lib/Kconfig | 5 +
lib/Makefile | 16 +++
lib/build_OID_registry | 209 ++++++++++++++++++++++++++++++++++++++++++
lib/oid_registry.c | 89 ++++++++++++++++++
6 files changed, 410 insertions(+), 1 deletion(-)
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 0000000..5928546
--- /dev/null
+++ b/include/linux/oid_registry.h
@@ -0,0 +1,90 @@
+/* ASN.1 Object identifier (OID) registry
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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_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_content_type, /* 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 */
+
+ OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */
+ OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */
+ OID_sha1, /* 1.3.14.3.2.26 */
+
+ /* 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 */
+
+ OID__NR
+};
+
+extern enum OID look_up_OID(const void *data, size_t datasize);
+
+#endif /* _LINUX_OID_REGISTRY_H */
diff --git a/lib/.gitignore b/lib/.gitignore
index 3bef1ea..09aae85 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -3,4 +3,4 @@
#
gen_crc32table
crc32table.h
-
+oid_registry_data.c
diff --git a/lib/Kconfig b/lib/Kconfig
index bb94c1b..a4009c2 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -396,4 +396,9 @@ config SIGNATURE
config LIBFDT
bool

+config OID_REGISTRY
+ tristate "Fast OID registry"
+ help
+ Enable fast lookup object identifier registry.
+
endmenu
diff --git a/lib/Makefile b/lib/Makefile
index 42d283e..b042896 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -150,3 +150,19 @@ quiet_cmd_crc32 = GEN $@

$(obj)/crc32table.h: $(obj)/gen_crc32table
$(call cmd,crc32)
+
+#
+# Build a fast OID lookip registry from include/linux/oid_registry.h
+#
+obj-$(CONFIG_OID_REGISTRY) += oid_registry.o
+
+$(obj)/oid_registry.c: $(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
diff --git a/lib/build_OID_registry b/lib/build_OID_registry
new file mode 100755
index 0000000..dfbdaab
--- /dev/null
+++ b/lib/build_OID_registry
@@ -0,0 +1,209 @@
+#!/usr/bin/perl -w
+#
+# Build a static ASN.1 Object Identified (OID) registry
+#
+# Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells ([email protected])
+#
+# 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.
+#
+
+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;
+
+print "Compiling ", $#names + 1, " OIDs\n";
+
+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 0000000..33cfd17
--- /dev/null
+++ b/lib/oid_registry.c
@@ -0,0 +1,89 @@
+/* ASN.1 Object identifier (OID) registry
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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.
+ */
+
+#include <linux/export.h>
+#include <linux/oid_registry.h>
+#include "oid_registry_data.c"
+
+/**
+ * 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);

2012-09-13 23:49:31

by David Howells

[permalink] [raw]
Subject: [PATCH 10/16] RSA: Fix signature verification for shorter signatures

gpg can produce a signature file where length of signature is less than the
modulus size because the amount of space an MPI takes up is kept as low as
possible by discarding leading zeros. This regularly happens for several
modules during the build.

Fix it by relaxing check in RSA verification code.

Thanks to Tomas Mraz and Miloslav Trmac for help.

Signed-off-by: Milan Broz <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/rsa.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)


diff --git a/crypto/asymmetric_keys/rsa.c b/crypto/asymmetric_keys/rsa.c
index 9b31ee2..4a6a069 100644
--- a/crypto/asymmetric_keys/rsa.c
+++ b/crypto/asymmetric_keys/rsa.c
@@ -224,15 +224,23 @@ static int RSA_verify_signature(const struct public_key *key,
return -ENOTSUPP;

/* (1) Check the signature size against the public key modulus size */
- k = (mpi_get_nbits(key->rsa.n) + 7) / 8;
+ k = mpi_get_nbits(key->rsa.n);
+ tsize = mpi_get_nbits(sig->rsa.s);

- tsize = (mpi_get_nbits(sig->rsa.s) + 7) / 8;
+ /* According to RFC 4880 sec 3.2, length of MPI is computed starting
+ * from most significant bit. So the RFC 3447 sec 8.2.2 size check
+ * must be relaxed to conform with shorter signatures - so we fail here
+ * only if signature length is longer than modulus size.
+ */
pr_devel("step 1: k=%zu size(S)=%zu\n", k, tsize);
- if (tsize != k) {
+ if (k < tsize) {
ret = -EBADMSG;
goto error;
}

+ /* Round up and convert to octets */
+ k = (k + 7) / 8;
+
/* (2b) Apply the RSAVP1 verification primitive to the public key */
ret = RSAVP1(key, sig->rsa.s, &m);
if (ret < 0)

2012-09-13 23:50:14

by David Howells

[permalink] [raw]
Subject: [PATCH 15/16] MPILIB: Provide a function to read raw data into an MPI

Provide a function to read raw data of a predetermined size into an MPI rather
than expecting the size to be encoded within the data. The data is assumed to
represent an unsigned integer, and the resulting MPI will be positive.

The function looks like this:

MPI mpi_read_raw_data(const void *, size_t);

This is useful for reading ASN.1 integer primitives where the length is encoded
in the ASN.1 metadata.

Signed-off-by: David Howells <[email protected]>
---

include/linux/mpi.h | 1 +
lib/mpi/mpicoder.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 56 insertions(+)


diff --git a/include/linux/mpi.h b/include/linux/mpi.h
index d02cca6..5af1b81 100644
--- a/include/linux/mpi.h
+++ b/include/linux/mpi.h
@@ -76,6 +76,7 @@ void mpi_swap(MPI a, MPI b);

/*-- mpicoder.c --*/
MPI do_encode_md(const void *sha_buffer, unsigned nbits);
+MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes);
MPI mpi_read_from_buffer(const void *buffer, unsigned *ret_nread);
int mpi_fromstr(MPI val, const char *str);
u32 mpi_get_keyid(MPI a, u32 *keyid);
diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c
index f0fa659..3962b7f 100644
--- a/lib/mpi/mpicoder.c
+++ b/lib/mpi/mpicoder.c
@@ -18,10 +18,65 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/

+#include <linux/bitops.h>
+#include <asm-generic/bitops/count_zeros.h>
#include "mpi-internal.h"

#define MAX_EXTERN_MPI_BITS 16384

+/**
+ * mpi_read_raw_data - Read a raw byte stream as a positive integer
+ * @xbuffer: The data to read
+ * @nbytes: The amount of data to read
+ */
+MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes)
+{
+ const uint8_t *buffer = xbuffer;
+ int i, j;
+ unsigned nbits, nlimbs;
+ mpi_limb_t a;
+ MPI val = NULL;
+
+ while (nbytes >= 0 && buffer[0] == 0) {
+ buffer++;
+ nbytes--;
+ }
+
+ nbits = nbytes * 8;
+ if (nbits > MAX_EXTERN_MPI_BITS) {
+ pr_info("MPI: mpi too large (%u bits)\n", nbits);
+ return NULL;
+ }
+ if (nbytes > 0)
+ nbits -= count_leading_zeros(buffer[0]);
+ else
+ nbits = 0;
+
+ nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB;
+ val = mpi_alloc(nlimbs);
+ if (!val)
+ return NULL;
+ val->nbits = nbits;
+ val->sign = 0;
+ val->nlimbs = nlimbs;
+
+ if (nbytes > 0) {
+ i = BYTES_PER_MPI_LIMB - nbytes % BYTES_PER_MPI_LIMB;
+ i %= BYTES_PER_MPI_LIMB;
+ for (j = nlimbs; j > 0; j--) {
+ a = 0;
+ for (; i < BYTES_PER_MPI_LIMB; i++) {
+ a <<= 8;
+ a |= *buffer++;
+ }
+ i = 0;
+ val->d[j - 1] = a;
+ }
+ }
+ return val;
+}
+EXPORT_SYMBOL_GPL(mpi_read_raw_data);
+
MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread)
{
const uint8_t *buffer = xbuffer;

2012-09-13 23:49:55

by David Howells

[permalink] [raw]
Subject: [PATCH 13/16] 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: David Howells <[email protected]>
---

include/linux/asn1.h | 67 ++
include/linux/asn1_ber_bytecode.h | 87 ++
init/Kconfig | 8
scripts/.gitignore | 1
scripts/Makefile | 2
scripts/Makefile.build | 11
scripts/asn1_compiler.c | 1544 +++++++++++++++++++++++++++++++++++++
7 files changed, 1720 insertions(+)
create mode 100644 include/linux/asn1.h
create mode 100644 include/linux/asn1_ber_bytecode.h
create mode 100644 scripts/asn1_compiler.c


diff --git a/include/linux/asn1.h b/include/linux/asn1.h
new file mode 100644
index 0000000..5c3f4e4
--- /dev/null
+++ b/include/linux/asn1.h
@@ -0,0 +1,67 @@
+/* ASN.1 BER/DER/CER encoding definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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 */
diff --git a/include/linux/asn1_ber_bytecode.h b/include/linux/asn1_ber_bytecode.h
new file mode 100644
index 0000000..945d44a
--- /dev/null
+++ b/include/linux/asn1_ber_bytecode.h
@@ -0,0 +1,87 @@
+/* ASN.1 BER/DER/CER parsing state machine internal definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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 */
diff --git a/init/Kconfig b/init/Kconfig
index af6c7f8..a31db1f 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1612,4 +1612,12 @@ config PADATA
depends on SMP
bool

+config ASN1
+ tristate "Simple ASN.1 grammar compiler and stream decoder"
+ 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"
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 65f362d..fb070fa 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -10,3 +10,4 @@ ihex2fw
recordmcount
docproc
sortextable
+asn1_compiler
diff --git a/scripts/Makefile b/scripts/Makefile
index a55b006..01e7adb 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -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)

diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index ff1720d..0e801c3 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -354,6 +354,17 @@ quiet_cmd_cpp_lds_S = LDS $@
$(obj)/%.lds: $(src)/%.lds.S FORCE
$(call if_changed_dep,cpp_lds_S)

+# ASN.1 grammar
+# ---------------------------------------------------------------------------
+quiet_cmd_asn1_compiler = ASN.1 $@
+ cmd_asn1_compiler = $(objtree)/scripts/asn1_compiler $< \
+ $(subst .h,.c,$@) $(subst .c,.h,$@)
+
+.PRECIOUS: $(objtree)/$(obj)/%-asn1.c $(objtree)/$(obj)/%-asn1.h
+
+$(obj)/%-asn1.c $(obj)/%-asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler
+ $(call cmd,asn1_compiler)
+
# Build the compiled-in targets
# ---------------------------------------------------------------------------

diff --git a/scripts/asn1_compiler.c b/scripts/asn1_compiler.c
new file mode 100644
index 0000000..77a1599
--- /dev/null
+++ b/scripts/asn1_compiler.c
@@ -0,0 +1,1544 @@
+/* Simplified ASN.1 notation parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.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_GeneralizedTime,
+ DIRECTIVE_GeneralString,
+ 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] = {
+ "ABSENT",
+ "ALL",
+ "ANY",
+ "APPLICATION",
+ "AUTOMATIC",
+ "BEGIN",
+ "BIT",
+ "BMPString",
+ "BOOLEAN",
+ "BY",
+ "CHARACTER",
+ "CHOICE",
+ "CLASS",
+ "COMPONENT",
+ "COMPONENTS",
+ "CONSTRAINED",
+ "CONTAINING",
+ "DEFAULT",
+ "DEFINED",
+ "DEFINITIONS",
+ "EMBEDDED",
+ "ENCODED",
+ "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",
+ "MINUS-INFINITY",
+ "NULL",
+ "NumericString",
+ "OBJECT",
+ "OCTET",
+ "OF",
+ "OPTIONAL",
+ "ObjectDescriptor",
+ "PATTERN",
+ "PDV",
+ "PLUS-INFINITY",
+ "PRESENT",
+ "PRIVATE",
+ "PrintableString",
+ "REAL",
+ "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;
+ unsigned char index;
+ char name[];
+};
+
+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;
+ const char *value;
+ struct type *type;
+};
+
+static struct token *token_list;
+static unsigned nr_tokens;
+
+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;
+
+ //printf("cmp(%*.*s,%s) = ",
+ // (int)token->size, (int)token->size, token->value,
+ // dir);
+
+ val = memcmp(token->value, dir, clen);
+ if (val != 0) {
+ //printf("%d [cmp]\n", val);
+ return val;
+ }
+
+ if (dlen == token->size) {
+ //printf("0\n");
+ return 0;
+ }
+ //printf("%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, *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;
+ tokens[tix].value = 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;
+
+ /* If it begins with a lowercase letter then
+ * it's an element name
+ */
+ if (islower(tokens[tix].value[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++].token_type = TOKEN_NUMBER;
+ continue;
+ }
+
+ if (nl - p >= 3) {
+ if (memcmp(p, "::=", 3) == 0) {
+ p += 3;
+ tokens[tix].size = 3;
+ tokens[tix++].token_type = TOKEN_ASSIGNMENT;
+ continue;
+ }
+ }
+
+ if (nl - p >= 2) {
+ if (memcmp(p, "({", 2) == 0) {
+ p += 2;
+ tokens[tix].size = 2;
+ tokens[tix++].token_type = TOKEN_OPEN_ACTION;
+ continue;
+ }
+ if (memcmp(p, "})", 2) == 0) {
+ p += 2;
+ tokens[tix].size = 2;
+ tokens[tix++].token_type = TOKEN_CLOSE_ACTION;
+ continue;
+ }
+ }
+
+ if (nl - p >= 1) {
+ tokens[tix].size = 1;
+ switch (*p) {
+ case '{':
+ p += 1;
+ tokens[tix++].token_type = TOKEN_OPEN_CURLY;
+ continue;
+ case '}':
+ p += 1;
+ tokens[tix++].token_type = TOKEN_CLOSE_CURLY;
+ continue;
+ case '[':
+ p += 1;
+ tokens[tix++].token_type = TOKEN_OPEN_SQUARE;
+ continue;
+ case ']':
+ p += 1;
+ tokens[tix++].token_type = TOKEN_CLOSE_SQUARE;
+ continue;
+ case ',':
+ p += 1;
+ 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;
+ printf("Extracted %u tokens\n", nr_tokens);
+
+#if 0
+ {
+ int n;
+ for (n = 0; n < nr_tokens; n++)
+ printf("Token %3u: '%*.*s'\n",
+ n,
+ (int)token_list[n].size, (int)token_list[n].size,
+ token_list[n].value);
+ }
+#endif
+}
+
+static void build_type_list(void);
+static void parse(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;
+ int fd;
+
+ if (argc != 4) {
+ fprintf(stderr, "Format: %s <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();
+
+ out = fopen(outputname, "w");
+ if (!out) {
+ perror(outputname);
+ exit(1);
+ }
+
+ hdr = fopen(headername, "w");
+ if (!out) {
+ 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_MARKED 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->value, (*b)->name->value,
+ (*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->value, type->name->value,
+ 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);
+
+ printf("Extracted %u types\n", nr_types);
+#if 0
+ for (n = 0; n < nr_types; n++) {
+ struct type *type = type_index[n];
+ printf("- %*.*s\n",
+ (int)type->name->size,
+ (int)type->name->size,
+ type->name->value);
+ }
+#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,
+ (int)cursor->size, (int)cursor->size, cursor->value);
+ exit(1);
+ }
+
+ } while (type++, !(type->flags & TYPE_STOP_MARKER));
+
+ printf("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,
+ (int)cursor->size, (int)cursor->size, cursor->value);
+ 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,
+ (int)cursor->size, (int)cursor->size, cursor->value);
+ exit(1);
+ }
+
+ element->tag &= ~0x1f;
+ element->tag |= strtoul(cursor->value, &p, 10);
+ if (p - cursor->value != 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,
+ (int)cursor->size, (int)cursor->size, cursor->value);
+ 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,
+ (int)cursor->size, (int)cursor->size, cursor->value);
+ 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,
+ (int)cursor->size, (int)cursor->size, cursor->value);
+ 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,
+ (int)cursor->size, (int)cursor->size, cursor->value);
+ exit(1);
+ }
+
+ action = malloc(sizeof(struct action) + cursor->size + 1);
+ if (!action) {
+ perror(NULL);
+ exit(1);
+ }
+ action->index = 0;
+ memcpy(action->name, cursor->value, cursor->size);
+ action->name[cursor->size] = 0;
+
+ 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,
+ (int)cursor->size, (int)cursor->size, cursor->value);
+ exit(1);
+ }
+ cursor++;
+ }
+
+ *_cursor = cursor;
+ return top;
+
+parse_error:
+ fprintf(stderr, "%s:%d: Unexpected token '%*.*s'\n",
+ filename, cursor->line,
+ (int)cursor->size, (int)cursor->size, cursor->value);
+ 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,
+ (int)cursor->size, (int)cursor->size, cursor->value);
+ 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,
+ (int)cursor->size, (int)cursor->size, cursor->value);
+ exit(1);
+ }
+ cursor++;
+
+ *_cursor = cursor;
+ return children;
+
+overrun_error:
+ fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename);
+ exit(1);
+}
+
+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 */
+ printf("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 */
+ printf("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;
+ }
+ 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;
+ 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",
+ (int)e->type_def->name->size, (int)e->type_def->name->size,
+ e->type_def->name->value);
+ }
+
+ /* 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,", cond, act);
+ if (e->name)
+ render_more(out, "\t\t// %*.*s",
+ (int)e->name->size, (int)e->name->size,
+ e->name->value);
+ 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;
+ }
+
+ if (e->name)
+ render_more(out, "\t\t// %*.*s",
+ (int)e->name->size, (int)e->name->size,
+ e->name->value);
+ render_more(out, "\n");
+
+ /* Render the tag */
+ if (!tag)
+ 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_ACT,\n");
+ 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",
+ (int)e->type_def->name->size,
+ (int)e->type_def->name->size,
+ e->type_def->name->value);
+ 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",
+ (int)e->type_def->name->size,
+ (int)e->type_def->name->size,
+ e->type_def->name->value);
+ 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, NULL);
+ 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);
+}

2012-09-13 23:50:23

by David Howells

[permalink] [raw]
Subject: [PATCH 16/16] X.509: Add a crypto key parser for binary (DER) X.509 certificates

Add a crypto key parser for binary (DER) encoded X.509 certificates. The
certificate is parsed and, if possible, the signature is verified.

An X.509 key can be added like this:

# keyctl padd crypto bar @s </tmp/x509.cert
15768135

and displayed like this:

# cat /proc/keys
00f09a47 I--Q--- 1 perm 39390000 0 0 asymmetri bar: X509.RSA e9fd6d08 []

Note that this only works with binary certificates. PEM encoded certificates
are ignored by the parser.

Note also that the X.509 key ID is not congruent with the PGP key ID, but for
the moment, they will match.

If a NULL or "" name is given to add_key(), then the parser will generate a key
description from the CertificateSerialNumber and Name fields of the
TBSCertificate:

00aefc4e I--Q--- 1 perm 39390000 0 0 asymmetri bfbc0cd76d050ea4:/C=GB/L=Cambridge/O=Red Hat/CN=kernel key: X509.RSA 0c688c7b []

Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/.gitignore | 1
crypto/asymmetric_keys/Kconfig | 10 +
crypto/asymmetric_keys/Makefile | 17 +
crypto/asymmetric_keys/x509.asn1 | 60 +++
crypto/asymmetric_keys/x509_cert_parser.c | 503 +++++++++++++++++++++++++++++
crypto/asymmetric_keys/x509_parser.h | 37 ++
crypto/asymmetric_keys/x509_public_key.c | 206 ++++++++++++
crypto/asymmetric_keys/x509_rsakey.asn1 | 4
8 files changed, 838 insertions(+)
create mode 100644 crypto/asymmetric_keys/.gitignore
create mode 100644 crypto/asymmetric_keys/x509.asn1
create mode 100644 crypto/asymmetric_keys/x509_cert_parser.c
create mode 100644 crypto/asymmetric_keys/x509_parser.h
create mode 100644 crypto/asymmetric_keys/x509_public_key.c
create mode 100644 crypto/asymmetric_keys/x509_rsakey.asn1


diff --git a/crypto/asymmetric_keys/.gitignore b/crypto/asymmetric_keys/.gitignore
new file mode 100644
index 0000000..ee32837
--- /dev/null
+++ b/crypto/asymmetric_keys/.gitignore
@@ -0,0 +1 @@
+*-asn1.[ch]
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 561759d..6d2c2ea 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -25,4 +25,14 @@ config PUBLIC_KEY_ALGO_RSA
help
This option enables support for the RSA algorithm (PKCS#1, RFC3447).

+config X509_CERTIFICATE_PARSER
+ tristate "X.509 certificate parser"
+ depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ select ASN1
+ select OID_REGISTRY
+ help
+ This option procides 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/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 7c92691..0727204 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -8,3 +8,20 @@ asymmetric_keys-y := asymmetric_type.o signature.o

obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o
+
+#
+# X.509 Certificate handling
+#
+obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o
+x509_key_parser-y := \
+ x509-asn1.o \
+ x509_rsakey-asn1.o \
+ x509_cert_parser.o \
+ x509_public_key.o
+
+$(obj)/x509_cert_parser.o: $(obj)/x509-asn1.h $(obj)/x509_rsakey-asn1.h
+$(obj)/x509-asn1.o: $(obj)/x509-asn1.c $(obj)/x509-asn1.h
+$(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h
+
+clean-files += x509-asn1.c x509-asn1.h
+clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h
diff --git a/crypto/asymmetric_keys/x509.asn1 b/crypto/asymmetric_keys/x509.asn1
new file mode 100644
index 0000000..a7ad4fc
--- /dev/null
+++ b/crypto/asymmetric_keys/x509.asn1
@@ -0,0 +1,60 @@
+Certificate ::= SEQUENCE {
+ tbsCertificate TBSCertificate ({ x509_note_tbs_certificate }),
+ signatureAlgorithm AlgorithmIdentifier,
+ signature BIT STRING ({ x509_note_signature })
+ } ({ x509_note_cert_start })
+
+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
+}
+
+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/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
new file mode 100644
index 0000000..cdcfce8
--- /dev/null
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -0,0 +1,503 @@
+/* X.509 certificate parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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.
+ */
+#undef DEBUG
+#define pr_fmt(fmt) "X.509: "fmt
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#include "public_key.h"
+#include "x509_parser.h"
+#include "x509-asn1.h"
+#include "x509_rsakey-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 */
+ enum OID last_oid; /* Last OID encountered */
+ enum OID algo_oid; /* Algorithm OID */
+ unsigned char nr_mpi; /* Number of MPIs stored */
+ int namesize; /* Usage of namebuffer */
+ char namebuffer[256]; /* Name building buffer */
+};
+
+/*
+ * Free an X.509 certificate
+ */
+void x509_free_certificate(struct x509_certificate *cert)
+{
+ if (cert) {
+ public_key_destroy(cert->pub);
+ kfree(cert->serial);
+ kfree(cert->issuer);
+ kfree(cert->subject);
+ kfree(cert->fingerprint);
+ kfree(cert->authority);
+ kfree(cert);
+ }
+}
+
+/*
+ * 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;
+ 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;
+ 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 public key */
+ ret = asn1_ber_decoder(&x509_rsakey_decoder, ctx,
+ ctx->key, ctx->key_size);
+ if (ret < 0)
+ goto error_decode;
+
+ kfree(ctx);
+ return cert;
+
+error_decode:
+ kfree(ctx);
+error_no_ctx:
+ x509_free_certificate(cert);
+error_no_cert:
+ return ERR_PTR(ret);
+}
+
+/*
+ * 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));
+ printk("X.509: Unknown OID: [%zu] %s: %d\n",
+ (unsigned long)value - ctx->data, buffer, ctx->last_oid);
+ }
+ 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(,%02x,%ld,%zu)!\n",
+ tag, (unsigned long)value - ctx->data, vlen);
+
+ ctx->cert->tbs = value;
+ ctx->cert->tbs_size = vlen;
+ return 0;
+}
+
+/*
+ * Note the start position of the certificate content with respect to the TBS
+ * data and use this to compute the length of the TBS header which is needed
+ * for the signature check.
+ */
+int x509_note_cert_start(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct x509_parse_context *ctx = context;
+ ptrdiff_t hdrsize;
+
+ pr_debug("x509_note_cert_start(,%02x,%ld,%zu)!\n",
+ tag, (unsigned long)value - ctx->data, vlen);
+
+ hdrsize = ctx->cert->tbs - value;
+ ctx->cert->tbs = value;
+ ctx->cert->tbs_size += hdrsize;
+ 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 = PKEY_HASH_MD5;
+ ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+ break;
+
+ case OID_sha1WithRSAEncryption:
+ ctx->cert->sig_hash_algo = PKEY_HASH_SHA1;
+ ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+ break;
+
+ case OID_sha256WithRSAEncryption:
+ ctx->cert->sig_hash_algo = PKEY_HASH_SHA256;
+ ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+ break;
+
+ case OID_sha384WithRSAEncryption:
+ ctx->cert->sig_hash_algo = PKEY_HASH_SHA384;
+ ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+ break;
+
+ case OID_sha512WithRSAEncryption:
+ ctx->cert->sig_hash_algo = PKEY_HASH_SHA512;
+ ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+ break;
+
+ case OID_sha224WithRSAEncryption:
+ ctx->cert->sig_hash_algo = PKEY_HASH_SHA224;
+ ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+ break;
+ }
+
+ 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("X.509: Got cert with pkey (%u) and sig (%u) algorithm OIDs\n",
+ ctx->algo_oid, ctx->last_oid);
+ return -EINVAL;
+ }
+
+ ctx->cert->sig = value;
+ ctx->cert->sig_size = vlen;
+ return 0;
+}
+
+/*
+ * Extract name segments into the assembly buffer and tag them with prefixes
+ * appropriate to the attribute OID.
+ */
+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;
+ const char *prefix;
+ char pbuf[4];
+
+ switch (ctx->last_oid) {
+ case OID_commonName: prefix = "CN"; break;
+ case OID_surname: prefix = "S"; break;
+ case OID_countryName: prefix = "C"; break;
+ case OID_locality: prefix = "L"; break;
+ case OID_stateOrProvinceName: prefix = "ST"; break;
+ case OID_organizationName: prefix = "O"; break;
+ case OID_organizationUnitName: prefix = "OU"; break;
+ case OID_title: prefix = "T"; break;
+ case OID_name: prefix = "N"; break;
+ case OID_givenName: prefix = "G"; break;
+ case OID_initials: prefix = "I"; break;
+ case OID_generationalQualifier: prefix = "GQ"; break;
+ default:
+ sprintf(pbuf, "?%u", ctx->last_oid);
+ prefix = pbuf;
+ break;
+ }
+
+ ctx->namesize += snprintf(ctx->namebuffer + ctx->namesize,
+ sizeof(ctx->namebuffer) - ctx->namesize,
+ "/%s=%*.*s",
+ prefix,
+ (int)vlen, (int)vlen, (const char *)value);
+ if (ctx->namesize > sizeof(ctx->namebuffer) - 1)
+ ctx->namesize = sizeof(ctx->namebuffer) - 1;
+
+ return 0;
+}
+
+/*
+ * Take a copy of the issuer name.
+ */
+int x509_note_issuer(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct x509_parse_context *ctx = context;
+
+ pr_debug("Issuer: %s\n", ctx->namebuffer);
+
+ ctx->cert->issuer = kmalloc(ctx->namesize + 1, GFP_KERNEL);
+ if (!ctx->cert->issuer)
+ return -ENOMEM;
+ memcpy(ctx->cert->issuer, ctx->namebuffer, ctx->namesize);
+ ctx->cert->issuer[ctx->namesize] = 0;
+ ctx->namesize = 0;
+ return 0;
+}
+
+/*
+ * Take a copy of the subject name.
+ */
+int x509_note_subject(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct x509_parse_context *ctx = context;
+ pr_debug("Subject: %s\n", ctx->namebuffer);
+
+ ctx->cert->subject = kmalloc(ctx->namesize + 1, GFP_KERNEL);
+ if (!ctx->cert->subject)
+ return -ENOMEM;
+ memcpy(ctx->cert->subject, ctx->namebuffer, ctx->namesize);
+ ctx->cert->subject[ctx->namesize] = 0;
+ ctx->namesize = 0;
+ 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;
+
+ if (ctx->last_oid != OID_rsaEncryption)
+ return -ENOPKG;
+
+ /* There seems to be an extraneous 0 byte on the front of the data */
+ ctx->cert->pkey_algo = PKEY_ALGO_RSA;
+ ctx->key = value + 1;
+ ctx->key_size = vlen - 1;
+ return 0;
+}
+
+/*
+ * Extract a RSA public key value
+ */
+int rsa_extract_mpi(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct x509_parse_context *ctx = context;
+ MPI mpi;
+
+ if (ctx->nr_mpi >= ARRAY_SIZE(ctx->cert->pub->mpi)) {
+ pr_err("Too many public keys in certificate\n");
+ return -EBADMSG;
+ }
+
+ mpi = mpi_read_raw_data(value, vlen);
+ if (!mpi)
+ return -ENOMEM;
+
+ ctx->cert->pub->mpi[ctx->nr_mpi++] = mpi;
+ return 0;
+}
+
+/*
+ * 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;
+ const unsigned char *v = value;
+ char *f;
+ int i;
+
+ pr_debug("Extension: %u\n", ctx->last_oid);
+
+ if (ctx->last_oid == OID_subjectKeyIdentifier) {
+ /* Get hold of the key fingerprint */
+ if (vlen < 3)
+ return -EBADMSG;
+ if (v[0] != ASN1_OTS || v[1] != vlen - 2)
+ return -EBADMSG;
+ v += 2;
+ vlen -= 2;
+
+ f = kmalloc(vlen * 2 + 1, GFP_KERNEL);
+ if (!f)
+ return -ENOMEM;
+ for (i = 0; i < vlen; i++)
+ sprintf(f + i * 2, "%02x", v[i]);
+ pr_debug("fingerprint %s\n", f);
+ ctx->cert->fingerprint = f;
+
+ ctx->cert->pub->key_id_size = i =
+ min(vlen, sizeof(ctx->cert->pub->key_id));
+ memcpy(ctx->cert->pub->key_id, v + vlen - i, i);
+ return 0;
+ }
+
+ if (ctx->last_oid == OID_authorityKeyIdentifier) {
+ /* Get hold of the CA key fingerprint */
+ if (vlen < 5)
+ return -EBADMSG;
+ if (v[0] != (ASN1_SEQ | (ASN1_CONS << 5)) ||
+ v[1] != vlen - 2 ||
+ v[2] != (ASN1_CONT << 6) ||
+ v[3] != vlen - 4)
+ return -EBADMSG;
+ v += 4;
+ vlen -= 4;
+
+ f = kmalloc(vlen * 2 + 1, GFP_KERNEL);
+ if (!f)
+ return -ENOMEM;
+ for (i = 0; i < vlen; i++)
+ sprintf(f + i * 2, "%02x", v[i]);
+ pr_debug("authority %s\n", f);
+ ctx->cert->authority = f;
+ return 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Record 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;
+ char *serial;
+
+ for (; vlen > 0; vlen--, value++)
+ if (*(const u8 *)value != 0)
+ break;
+
+ serial = kmalloc(vlen * 2 + 1, GFP_KERNEL);
+ if (!serial)
+ return -ENOMEM;
+ ctx->cert->serial = serial;
+
+ /* Note: this limits the field width to a maximum of 64 */
+ sprintf(serial, "%*phN", (int)vlen, value);
+
+ pr_devel("Serial: %s\n", serial);
+ return 0;
+}
+
+/*
+ * Record a certificate time.
+ */
+static int x509_note_time(time_t *_time, size_t hdrlen,
+ unsigned char tag,
+ const unsigned char *value, size_t vlen)
+{
+ unsigned YY, MM, DD, hh, mm, ss;
+ const unsigned char *p = value;
+
+#define dec2bin(X) ((X) - '0')
+#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;
+ YY = DD2bin(p);
+ if (YY > 50)
+ YY += 1900;
+ else
+ YY += 2000;
+ } else if (tag == ASN1_GENTIM) {
+ /* GenTime: YYYYMMDDHHMMSSZ */
+ if (vlen != 15)
+ goto unsupported_time;
+ YY = DD2bin(p) * 100 + DD2bin(p);
+ } else {
+ goto unsupported_time;
+ }
+
+ MM = DD2bin(p);
+ DD = DD2bin(p);
+ hh = DD2bin(p);
+ mm = DD2bin(p);
+ ss = DD2bin(p);
+
+ if (*p != 'Z')
+ goto unsupported_time;
+
+ *_time = mktime(YY, MM, DD, hh, mm, ss);
+ return 0;
+
+unsupported_time:
+ pr_warn("X.509: Got unsupported time [tag %02x]: '%*.*s'\n",
+ tag, (int)vlen, (int)vlen, value);
+ return -EBADMSG;
+}
+
+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_note_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_note_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen);
+}
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
new file mode 100644
index 0000000..b38789a
--- /dev/null
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -0,0 +1,37 @@
+/* X.509 certificate parser internal definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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.
+ */
+
+#include <crypto/public_key.h>
+
+struct x509_certificate {
+ struct x509_certificate *next;
+ struct public_key *pub; /* Public key details */
+ char *serial; /* Certificate serial number */
+ char *issuer; /* Name of certificate issuer */
+ char *subject; /* Name of certificate subject */
+ char *fingerprint; /* Key fingerprint as hex */
+ char *authority; /* Authority key fingerprint as hex */
+ time_t valid_from;
+ time_t valid_to;
+ enum pkey_algo pkey_algo : 8; /* Public key algorithm */
+ enum pkey_algo sig_pkey_algo : 8; /* Signature public key algorithm */
+ enum pkey_hash_algo sig_hash_algo : 8; /* Signature hash algorithm */
+ const void *tbs; /* Signed data */
+ size_t tbs_size; /* Size of signed data */
+ const void *sig; /* Signature data */
+ size_t sig_size; /* Size of sigature */
+};
+
+/*
+ * 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);
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
new file mode 100644
index 0000000..a44c22c
--- /dev/null
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -0,0 +1,206 @@
+/* Instantiate a public key crypto key from an X.509 Certificate
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "X.509: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/mpi.h>
+#include <linux/asn1_decoder.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+#include <crypto/hash.h>
+#include "asymmetric_keys.h"
+#include "public_key.h"
+#include "x509_parser.h"
+
+static const
+struct public_key_algorithm *x509_public_key_algorithms[PKEY_ALGO__LAST] = {
+ [PKEY_ALGO_DSA] = NULL,
+#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \
+ defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE)
+ [PKEY_ALGO_RSA] = &RSA_public_key_algorithm,
+#endif
+};
+
+/*
+ * Check the signature on a certificate using the provided public key
+ */
+static int x509_check_signature(const struct public_key *pub,
+ const struct x509_certificate *cert)
+{
+ struct public_key_signature *sig;
+ struct crypto_shash *tfm;
+ struct shash_desc *desc;
+ size_t digest_size, desc_size;
+ int ret;
+
+ pr_devel("==>%s()\n", __func__);
+
+ /* Allocate the hashing algorithm we're going to need and find out how
+ * big the hash operational data will be.
+ */
+ tfm = crypto_alloc_shash(pkey_hash_algo[cert->sig_hash_algo], 0, 0);
+ if (IS_ERR(tfm))
+ return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
+
+ desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+ digest_size = crypto_shash_digestsize(tfm);
+
+ /* We allocate the hash operational data storage on the end of our
+ * context data.
+ */
+ ret = -ENOMEM;
+ sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL);
+ if (!sig)
+ goto error_no_sig;
+
+ sig->pkey_hash_algo = cert->sig_hash_algo;
+ sig->digest = (u8 *)sig + sizeof(*sig) + desc_size;
+ sig->digest_size = digest_size;
+
+ desc = (void *)sig + sizeof(*sig);
+ desc->tfm = tfm;
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ ret = crypto_shash_init(desc);
+ if (ret < 0)
+ goto error;
+
+ ret = -ENOMEM;
+ sig->rsa.s = mpi_read_raw_data(cert->sig, cert->sig_size);
+ if (!sig->rsa.s)
+ goto error;
+
+ ret = crypto_shash_update(desc, cert->tbs, cert->tbs_size);
+ if (ret < 0)
+ goto error_mpi;
+
+ sig->digest[0] = 0xa5;
+ sig->digest[1] = 0x5a;
+
+ ret = crypto_shash_final(desc, sig->digest);
+ if (ret < 0)
+ goto error_mpi;
+
+ ret = pub->algo->verify_signature(pub, sig);
+
+ pr_debug("Cert Verification: %d\n", ret);
+
+error_mpi:
+ mpi_free(sig->rsa.s);
+error:
+ kfree(sig);
+error_no_sig:
+ crypto_free_shash(tfm);
+
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
+}
+
+/*
+ * Attempt to parse a data blob for a key as an X509 certificate.
+ */
+static int x509_key_preparse(struct key_preparsed_payload *prep)
+{
+ struct x509_certificate *cert;
+ time_t now;
+ size_t srlen, sulen;
+ char *desc = NULL;
+ 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);
+ pr_devel("Cert Key Algo: %s\n", pkey_algo[cert->pkey_algo]);
+ pr_devel("Cert Valid: %lu - %lu\n", cert->valid_from, cert->valid_to);
+ pr_devel("Cert Signature: %s + %s\n",
+ pkey_algo[cert->sig_pkey_algo],
+ pkey_hash_algo[cert->sig_hash_algo]);
+
+ now = CURRENT_TIME.tv_sec;
+ if (now < cert->valid_from) {
+ pr_warn("Cert %s is not yet valid\n", cert->fingerprint);
+ ret = -EKEYREJECTED;
+ goto error_free_cert;
+ }
+ if (now >= cert->valid_to) {
+ pr_warn("Cert %s has expired\n", cert->fingerprint);
+ ret = -EKEYEXPIRED;
+ goto error_free_cert;
+ }
+
+ cert->pub->algo = x509_public_key_algorithms[cert->pkey_algo];
+ cert->pub->id_type = PKEY_ID_X509;
+
+ /* Check the signature on the key */
+ if (strcmp(cert->fingerprint, cert->authority) == 0) {
+ ret = x509_check_signature(cert->pub, cert);
+ if (ret < 0)
+ goto error_free_cert;
+ }
+
+ /* Propose a description */
+ srlen = strlen(cert->serial);
+ sulen = strlen(cert->subject);
+ ret = -ENOMEM;
+ desc = kmalloc(srlen + sulen + 2, GFP_KERNEL);
+ if (!desc)
+ goto error_free_cert;
+ memcpy(desc, cert->serial, srlen);
+ desc[srlen] = ':';
+ memcpy(desc + srlen + 1, cert->subject, sulen);
+ desc[srlen + sulen + 1] = 0;
+
+ /* We're pinning the module by being linked against it */
+ __module_get(public_key_subtype.owner);
+ prep->type_data[0] = &public_key_subtype;
+ prep->type_data[1] = cert->fingerprint;
+ prep->payload = cert->pub;
+ prep->description = desc;
+ prep->quotalen = 100;
+
+ /* We've finished with the certificate */
+ cert->pub = NULL;
+ cert->fingerprint = NULL;
+ desc = NULL;
+ ret = 0;
+
+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);
diff --git a/crypto/asymmetric_keys/x509_rsakey.asn1 b/crypto/asymmetric_keys/x509_rsakey.asn1
new file mode 100644
index 0000000..4ec7cc6
--- /dev/null
+++ b/crypto/asymmetric_keys/x509_rsakey.asn1
@@ -0,0 +1,4 @@
+RSAPublicKey ::= SEQUENCE {
+ modulus INTEGER ({ rsa_extract_mpi }), -- n
+ publicExponent INTEGER ({ rsa_extract_mpi }) -- e
+ }

2012-09-13 23:50:05

by David Howells

[permalink] [raw]
Subject: [PATCH 14/16] X.509: Add an ASN.1 decoder

Add an ASN.1 BER/DER/CER decoder. This uses the bytecode from the ASN.1
compiler in the previous patch to inform it as to what to expect to find in the
encoded byte stream. The output from the compiler also tells it what functions
to call on what tags, thus allowing the caller to retrieve information.

The decoder is called as follows:

int asn1_decoder(const struct asn1_decoder *decoder,
void *context,
const unsigned char *data,
size_t datalen);

The decoder argument points to the bytecode from the ASN.1 compiler. context
is the caller's context and is passed to the action functions. data and
datalen define the byte stream to be decoded.


Note that the decoder is currently limited to datalen being less than 64K.
This reduces the amount of stack space used by the decoder because ASN.1 is a
nested construct. Similarly, the decoder is limited to a maximum of 10 levels
of constructed data outside of a leaf node also in an effort to keep stack
usage down.

These restrictions can be raised if necessary.

Signed-off-by: David Howells <[email protected]>
---

include/linux/asn1_decoder.h | 24 ++
lib/Makefile | 2
lib/asn1_decoder.c | 473 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 499 insertions(+)
create mode 100644 include/linux/asn1_decoder.h
create mode 100644 lib/asn1_decoder.c


diff --git a/include/linux/asn1_decoder.h b/include/linux/asn1_decoder.h
new file mode 100644
index 0000000..fa2ff5b
--- /dev/null
+++ b/include/linux/asn1_decoder.h
@@ -0,0 +1,24 @@
+/* ASN.1 decoder
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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_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/lib/Makefile b/lib/Makefile
index b042896..ca856b6 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -140,6 +140,8 @@ $(foreach file, $(libfdt_files), \
$(eval CFLAGS_$(file) = -I$(src)/../scripts/dtc/libfdt))
lib-$(CONFIG_LIBFDT) += $(libfdt_files)

+obj-$(CONFIG_ASN1) += asn1_decoder.o
+
hostprogs-y := gen_crc32table
clean-files := crc32table.h

diff --git a/lib/asn1_decoder.c b/lib/asn1_decoder.c
new file mode 100644
index 0000000..8179f3c
--- /dev/null
+++ b/lib/asn1_decoder.c
@@ -0,0 +1,473 @@
+/* Decoder for ASN.1 BER/DER/CER encoded bytestream
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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.
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#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_ACT] = 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_ACT] = 1 + 1,
+ [ASN1_OP_COND_FAIL] = 1,
+ [ASN1_OP_COMPLETE] = 1,
+ [ASN1_OP_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
+ */
+static int asn1_find_indefinite_length(const unsigned char *data, size_t datalen,
+ const char **_errmsg, size_t *_err_dp)
+{
+ unsigned char tag, tmp;
+ size_t dp = 0, 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 == 0) {
+ /* It appears to be an EOC. */
+ if (data[dp++] != 0)
+ goto invalid_eoc;
+ if (--indef_level <= 0)
+ return dp;
+ goto next_tag;
+ }
+
+ if (unlikely((tag & 0x1f) == 0x1f)) {
+ 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) {
+ dp += len;
+ goto next_tag;
+ }
+
+ if (unlikely(len == 0x80)) {
+ /* 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(size_t) - 1))
+ goto length_too_long;
+ if (unlikely(n > datalen - dp))
+ goto data_overrun_error;
+ for (len = 0; n > 0; n--) {
+ len <<= 8;
+ len |= data[dp++];
+ }
+ 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:
+ *_err_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
+ * @datasize: 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;
+ const char *errmsg;
+ size_t pc = 0, dp = 0, tdp = 0, len = 0, hdr = 0;
+ int ret;
+
+ unsigned char flags = 0;
+#define FLAG_INDEFINITE_LENGTH 0x01
+#define FLAG_MATCHED 0x02
+#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];
+#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) ||
+ dp == datalen) {
+ pc += asn1_op_lengths[op];
+ goto next_op;
+ }
+
+ flags = 0;
+ hdr = dp;
+
+ /* Extract a tag from the data */
+ if (unlikely(dp >= datalen - 1))
+ goto data_overrun_error;
+ tag = data[dp++];
+ if (unlikely((tag & 0x1f) == 0x1f))
+ 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 == 0x80)) {
+ /* 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(dp >= datalen - n))
+ goto data_overrun_error;
+ for (len = 0; n > 0; n--) {
+ len <<= 8;
+ len |= data[dp++];
+ }
+ 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;
+ 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_ANY_ACT:
+ case ASN1_OP_COND_MATCH_ANY_ACT:
+ ret = actions[machine[pc + 1]](context, dp - hdr, tag, data + dp, len);
+ if (ret < 0)
+ return ret;
+ goto skip_data;
+
+ case ASN1_OP_MATCH_ACT:
+ case ASN1_OP_MATCH_ACT_OR_SKIP:
+ case ASN1_OP_COND_MATCH_ACT_OR_SKIP:
+ ret = actions[machine[pc + 2]](context, dp - hdr, tag, data + dp, len);
+ if (ret < 0)
+ return ret;
+ goto skip_data;
+
+ case ASN1_OP_MATCH:
+ case ASN1_OP_MATCH_OR_SKIP:
+ case ASN1_OP_MATCH_ANY:
+ case ASN1_OP_COND_MATCH_OR_SKIP:
+ case ASN1_OP_COND_MATCH_ANY:
+ skip_data:
+ if (!(flags & FLAG_CONS)) {
+ if (flags & FLAG_INDEFINITE_LENGTH) {
+ len = asn1_find_indefinite_length(
+ data + dp, datalen - dp, &errmsg, &dp);
+ if (len < 0)
+ goto error;
+ }
+ pr_debug("- LEAF: %zu\n", len);
+ 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;
+ 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];
+ 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, dp - hdr, 0, data + tdp, len);
+ }
+ pc += asn1_op_lengths[op];
+ goto next_op;
+
+ case ASN1_OP_ACT:
+ ret = actions[machine[pc + 1]](context, dp - hdr, tag, data + tdp, len);
+ pc += asn1_op_lengths[op];
+ goto next_op;
+
+ case ASN1_OP_RETURN:
+ if (unlikely(jsp <= 0))
+ goto jump_stack_underflow;
+ pc = jump_stack[--jsp];
+ goto next_op;
+
+ default:
+ break;
+ }
+
+ /* Shouldn't reach here */
+ pr_err("ASN.1 decoder error: Found reserved opcode (%u)\n", op);
+ 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);

2012-09-13 23:49:47

by David Howells

[permalink] [raw]
Subject: [PATCH 12/16] X.509: Add utility functions to render OIDs as strings

Add a pair of utility functions to render OIDs as strings. The first takes an
encoded OID and turns it into a "a.b.c.d" form string:

int sprint_oid(const void *data, size_t datasize,
char *buffer, size_t bufsize);

The second takes an OID enum index and calls the first on the data held
therein:

int sprint_OID(enum OID oid, char *buffer, size_t bufsize);

Signed-off-by: David Howells <[email protected]>
---

include/linux/oid_registry.h | 2 +
lib/oid_registry.c | 81 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 83 insertions(+)


diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
index 5928546..6926db7 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -86,5 +86,7 @@ enum OID {
};

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/oid_registry.c b/lib/oid_registry.c
index 33cfd17..d8de11f 100644
--- a/lib/oid_registry.c
+++ b/lib/oid_registry.c
@@ -11,6 +11,9 @@

#include <linux/export.h>
#include <linux/oid_registry.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
#include "oid_registry_data.c"

/**
@@ -87,3 +90,81 @@ enum OID look_up_OID(const void *data, size_t datasize)
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)
+ return -EBADMSG;
+
+ n = *v++;
+ ret = count = snprintf(buffer, bufsize, "%u.%u", n / 40, n % 40);
+ buffer += count;
+ bufsize -= count;
+ if (bufsize == 0)
+ return -ENOBUFS;
+
+ while (v < end) {
+ num = 0;
+ n = *v++;
+ if (!(n & 0x80)) {
+ num = n;
+ } else {
+ num = n & 0x7f;
+ do {
+ if (v >= end)
+ return -EBADMSG;
+ n = *v++;
+ num <<= 7;
+ num |= n & 0x7f;
+ } while (n & 0x80);
+ }
+ ret += count = snprintf(buffer, bufsize, ".%lu", num);
+ buffer += count;
+ bufsize -= count;
+ if (bufsize == 0)
+ return -ENOBUFS;
+ }
+
+ return ret;
+}
+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);

2012-09-13 23:49:06

by David Howells

[permalink] [raw]
Subject: [PATCH 07/16] KEYS: Provide signature verification with an asymmetric key

Provide signature verification using an asymmetric-type key to indicate the
public key to be used.

The API is a single function that can be found in crypto/public_key.h:

int verify_signature(const struct key *key,
const struct public_key_signature *sig)

The first argument is the appropriate key to be used and the second argument
is the parsed signature data:

struct public_key_signature {
u8 *digest;
u16 digest_size;
enum pkey_hash_algo pkey_hash_algo : 8;
union {
MPI mpi[2];
struct {
MPI s; /* m^d mod n */
} rsa;
struct {
MPI r;
MPI s;
} dsa;
};
};

This should be filled in prior to calling the function. The hash algorithm
should already have been called and the hash finalised and the output should
be in a buffer pointed to by the 'digest' member.

Any extra data to be added to the hash by the hash format (eg. PGP) should
have been added by the caller prior to finalising the hash.

It is assumed that the signature is made up of a number of MPI values. If an
algorithm becomes available for which this is not the case, the above structure
will have to change.

It is also assumed that it will have been checked that the signature algorithm
matches the key algorithm.

Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/Makefile | 2 +
crypto/asymmetric_keys/signature.c | 49 ++++++++++++++++++++++++++++++++++++
include/crypto/public_key.h | 4 +++
3 files changed, 54 insertions(+), 1 deletion(-)
create mode 100644 crypto/asymmetric_keys/signature.c


diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 5ed46ee..8dcdf0c 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -4,6 +4,6 @@

obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o

-asymmetric_keys-y := asymmetric_type.o
+asymmetric_keys-y := asymmetric_type.o signature.o

obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c
new file mode 100644
index 0000000..50b3f88
--- /dev/null
+++ b/crypto/asymmetric_keys/signature.c
@@ -0,0 +1,49 @@
+/* Signature verification with an asymmetric key
+ *
+ * See Documentation/security/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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.
+ */
+
+#include <keys/asymmetric-subtype.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <crypto/public_key.h>
+#include "asymmetric_keys.h"
+
+/**
+ * verify_signature - Initiate the use of an asymmetric key to verify a signature
+ * @key: The asymmetric key to verify against
+ * @sig: The signature to check
+ *
+ * Returns 0 if successful or else an error.
+ */
+int verify_signature(const struct key *key,
+ const struct public_key_signature *sig)
+{
+ const struct asymmetric_key_subtype *subtype;
+ 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)
+ return -EINVAL;
+ if (!subtype->verify_signature)
+ return -ENOTSUPP;
+
+ ret = subtype->verify_signature(key, sig);
+
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(verify_signature);
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index 65d9879..eb25eb2 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -106,4 +106,8 @@ struct public_key_signature {
};
};

+struct key;
+extern int verify_signature(const struct key *key,
+ const struct public_key_signature *sig);
+
#endif /* _LINUX_PUBLIC_KEY_H */

2012-09-13 23:48:40

by David Howells

[permalink] [raw]
Subject: [PATCH 04/16] KEYS: Implement asymmetric key type

Create a key type that can be used to represent an asymmetric key type for use
in appropriate cryptographic operations, such as encryption, decryption,
signature generation and signature verification.

The key type is "asymmetric" and can provide access to a variety of
cryptographic algorithms.

Possibly, this would be better as "public_key" - but that has the disadvantage
that "public key" is an overloaded term.

Signed-off-by: David Howells <[email protected]>
---

crypto/Kconfig | 1
crypto/Makefile | 1
crypto/asymmetric_keys/Kconfig | 13 +++
crypto/asymmetric_keys/Makefile | 7 +
crypto/asymmetric_keys/asymmetric_keys.h | 15 +++
crypto/asymmetric_keys/asymmetric_type.c | 156 ++++++++++++++++++++++++++++++
include/keys/asymmetric-subtype.h | 55 +++++++++++
include/keys/asymmetric-type.h | 25 +++++
8 files changed, 273 insertions(+)
create mode 100644 crypto/asymmetric_keys/Kconfig
create mode 100644 crypto/asymmetric_keys/Makefile
create mode 100644 crypto/asymmetric_keys/asymmetric_keys.h
create mode 100644 crypto/asymmetric_keys/asymmetric_type.c
create mode 100644 include/keys/asymmetric-subtype.h
create mode 100644 include/keys/asymmetric-type.h


diff --git a/crypto/Kconfig b/crypto/Kconfig
index a323805..1ca0b24 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -1043,5 +1043,6 @@ config CRYPTO_USER_API_SKCIPHER
key cipher algorithms.

source "drivers/crypto/Kconfig"
+source crypto/asymmetric_keys/Kconfig

endif # if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index 30f33d6..ced472e 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -96,3 +96,4 @@ obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
#
obj-$(CONFIG_XOR_BLOCKS) += xor.o
obj-$(CONFIG_ASYNC_CORE) += async_tx/
+obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys/
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
new file mode 100644
index 0000000..cad29b3
--- /dev/null
+++ b/crypto/asymmetric_keys/Kconfig
@@ -0,0 +1,13 @@
+menuconfig ASYMMETRIC_KEY_TYPE
+ tristate "Asymmetric (public-key cryptographic) key type"
+ depends on KEYS
+ 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
+
+
+endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
new file mode 100644
index 0000000..b725bcc
--- /dev/null
+++ b/crypto/asymmetric_keys/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for asymmetric cryptographic keys
+#
+
+obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
+
+asymmetric_keys-y := asymmetric_type.o
diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h
new file mode 100644
index 0000000..515b634
--- /dev/null
+++ b/crypto/asymmetric_keys/asymmetric_keys.h
@@ -0,0 +1,15 @@
+/* Internal definitions for asymmetric key type
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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.
+ */
+
+static inline const char *asymmetric_key_id(const struct key *key)
+{
+ return key->type_data.p[1];
+}
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
new file mode 100644
index 0000000..bfb0424
--- /dev/null
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -0,0 +1,156 @@
+/* Asymmetric public-key cryptography key type
+ *
+ * See Documentation/security/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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.
+ */
+#include <keys/asymmetric-subtype.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "asymmetric_keys.h"
+
+MODULE_LICENSE("GPL");
+
+/*
+ * Match asymmetric keys on (part of) their name
+ * We have some shorthand methods for matching keys. We allow:
+ *
+ * "<desc>" - request a key by description
+ * "id:<id>" - request a key matching the ID
+ * "<subtype>:<id>" - request a key of a subtype
+ */
+static int asymmetric_key_match(const struct key *key, const void *description)
+{
+ const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
+ const char *spec = description;
+ const char *id, *kid;
+ ptrdiff_t speclen;
+ size_t idlen, kidlen;
+
+ if (!subtype || !spec || !*spec)
+ return 0;
+
+ /* See if the full key description matches as is */
+ if (key->description && strcmp(key->description, description) == 0)
+ return 1;
+
+ /* All tests from here on break the criterion description into a
+ * specifier, a colon and then an identifier.
+ */
+ id = strchr(spec, ':');
+ if (!id)
+ return 0;
+
+ speclen = id - spec;
+ id++;
+
+ /* Anything after here requires a partial match on the ID string */
+ kid = asymmetric_key_id(key);
+ if (!kid)
+ return 0;
+
+ idlen = strlen(id);
+ kidlen = strlen(kid);
+ if (idlen > kidlen)
+ return 0;
+
+ kid += kidlen - idlen;
+ if (strcasecmp(id, kid) != 0)
+ return 0;
+
+ if (speclen == 2 &&
+ memcmp(spec, "id", 2) == 0)
+ return 1;
+
+ if (speclen == subtype->name_len &&
+ memcmp(spec, subtype->name, speclen) == 0)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * 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 char *kid = asymmetric_key_id(key);
+ size_t n;
+
+ seq_puts(m, key->description);
+
+ if (subtype) {
+ seq_puts(m, ": ");
+ subtype->describe(key, m);
+
+ if (kid) {
+ seq_putc(m, ' ');
+ n = strlen(kid);
+ if (n <= 8)
+ seq_puts(m, kid);
+ else
+ seq_puts(m, kid + n - 8);
+ }
+
+ seq_puts(m, " [");
+ /* put something here to indicate the key's capabilities */
+ seq_putc(m, ']');
+ }
+}
+
+/*
+ * Instantiate a asymmetric_key defined key. The key was preparsed, so we just
+ * have to transfer the data here.
+ */
+static int asymmetric_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
+{
+ return -EOPNOTSUPP;
+}
+
+/*
+ * 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);
+ if (subtype) {
+ subtype->destroy(key->payload.data);
+ module_put(subtype->owner);
+ key->type_data.p[0] = NULL;
+ }
+ kfree(key->type_data.p[1]);
+ key->type_data.p[1] = NULL;
+}
+
+struct key_type key_type_asymmetric = {
+ .name = "asymmetric",
+ .instantiate = asymmetric_key_instantiate,
+ .match = asymmetric_key_match,
+ .destroy = asymmetric_key_destroy,
+ .describe = asymmetric_key_describe,
+};
+EXPORT_SYMBOL_GPL(key_type_asymmetric);
+
+/*
+ * 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);
diff --git a/include/keys/asymmetric-subtype.h b/include/keys/asymmetric-subtype.h
new file mode 100644
index 0000000..4b840e8
--- /dev/null
+++ b/include/keys/asymmetric-subtype.h
@@ -0,0 +1,55 @@
+/* Asymmetric public-key cryptography key subtype
+ *
+ * See Documentation/security/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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 _KEYS_ASYMMETRIC_SUBTYPE_H
+#define _KEYS_ASYMMETRIC_SUBTYPE_H
+
+#include <linux/seq_file.h>
+#include <keys/asymmetric-type.h>
+
+struct public_key_signature;
+
+/*
+ * Keys of this type declare a subtype that indicates the handlers and
+ * capabilities.
+ */
+struct asymmetric_key_subtype {
+ struct module *owner;
+ const char *name;
+ unsigned short name_len; /* length of name */
+
+ /* Describe a key of this subtype for /proc/keys */
+ void (*describe)(const struct key *key, struct seq_file *m);
+
+ /* Destroy a key of this subtype */
+ void (*destroy)(void *payload);
+
+ /* Verify the signature on a key of this subtype (optional) */
+ int (*verify_signature)(const struct key *key,
+ const struct public_key_signature *sig);
+};
+
+/**
+ * asymmetric_key_subtype - Get the subtype from an asymmetric key
+ * @key: The key of interest.
+ *
+ * Retrieves and returns the subtype pointer of the asymmetric key from the
+ * type-specific data attached to the key.
+ */
+static inline
+struct asymmetric_key_subtype *asymmetric_key_subtype(const struct key *key)
+{
+ return key->type_data.p[0];
+}
+
+#endif /* _KEYS_ASYMMETRIC_SUBTYPE_H */
diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h
new file mode 100644
index 0000000..7dd4734
--- /dev/null
+++ b/include/keys/asymmetric-type.h
@@ -0,0 +1,25 @@
+/* Asymmetric Public-key cryptography key type interface
+ *
+ * See Documentation/security/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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 _KEYS_ASYMMETRIC_TYPE_H
+#define _KEYS_ASYMMETRIC_TYPE_H
+
+#include <linux/key-type.h>
+
+extern struct key_type key_type_asymmetric;
+
+/*
+ * The payload is at the discretion of the subtype.
+ */
+
+#endif /* _KEYS_ASYMMETRIC_TYPE_H */

2012-09-14 09:39:30

by Alan

[permalink] [raw]
Subject: Re: [PATCH 14/16] X.509: Add an ASN.1 decoder

On Fri, 14 Sep 2012 00:50:05 +0100
David Howells <[email protected]> wrote:

> Add an ASN.1 BER/DER/CER decoder. This uses the bytecode from the ASN.1
> compiler in the previous patch to inform it as to what to expect to find in the
> encoded byte stream. The output from the compiler also tells it what functions
> to call on what tags, thus allowing the caller to retrieve information.

Why do this in the kernel.That appears to be completely insane. Can you
prove it runs in a short bounded time for all inputs, has it been fuzz
tested extensively ?

This kind of crap belongs in user space. Parse it in userspace, pass same
structures and objects to the kernel.

Alan

2012-09-18 17:34:33

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 14/16] X.509: Add an ASN.1 decoder

Alan Cox <[email protected]> wrote:

> Why do this in the kernel.That appears to be completely insane.

A number of reasons:

(1) The UEFI signature/key database may contain ASN.1 X.509 certificates and
we may need to use those very early in the boot process, during initrd.

(2) Even if userspace is available, offloading the key parsing to userspace
means we have to have some way to trust what we get back.

(3) Giving the kernel ASN.1 X.509 certs allows the kernel to verify the
signature on that cert against a key it gets from the UEFI db. For that,
you need the raw cert.

> Can you prove it runs in a short bounded time for all inputs,

Possibly. I'll have to think about that. It may be relatively
straightforward.

(1) The ASN.1 decoder is limited (currently) to a maximum of 64k of data to
parse.

(2) The decoder never goes backward through the data.

(3) The decoder has a strictly limited recursion/nesting stack.

The decoder uses a state machine of a sort produced by the compiler.

(1) Most nodes in this only have one transition.

(2) Simple optional nodes are simply marked skippable.

(3) There are some nodes that are like subroutine calls (for multiple-use
constructed types), but these must return to the point directly after the
call.

(4) There are some nodes that have two transitions. These are used for
optional constructed type values (particularly multiple-use ones).
Basically, they are jump-to-subroutine or skip. The return must go back
to the next node.

Writing a perl script to check the sanity of the compiler output should be easy
enough, though it would have to assume that the driver is sane.

> has it been fuzz tested extensively ?

Fuzz testing from a script is very easy, eg:

#!/bin/sh
cd /tmp
sync
declare -i n i j k
while true
do
n=$RANDOM
j=$RANDOM
j=j%10
k=0
echo $n $j
dd if=/dev/urandom of=/tmp/data bs=$n count=1
for ((i=1; i<n; i=i+k))
do
dd if=/tmp/data bs=$i count=1 2>/dev/null |
keyctl padd asymmetric foo @s 2>/dev/null
k=k+1
if [ $k -eq 10 ]
then
echo -n .
k=0
fi
done
echo
done

Though I haven't done a great deal of such testing as I want to be able to use
my test machine for other stuff too. I can, however, run multiple fuzzers simultaneously.

I have also passed a number of bits of X.509 and PKCS#7 through it, and also
some valid ASN.1 that isn't what the decoder is expecting.

#!/bin/sh

file=/tmp/x509cert
if [ "$1" != "" ]
then
file=$1
fi

cd /tmp
sync

while true
do
openssl req -new -x509 -outform PEM -keyout $file.pem -nodes -subj "/CN=GB/O=Red Hat/OU=Magrathea/CN=Slartibartfast" -out $file.x509 || exit $?

openssl x509 -in $file.x509 -inform PEM -outform DER >$file.x509.asn1 || exit $?
keyctl padd asymmetric bar @s <$file.x509.asn1 || exit $?

n=$RANDOM
if [ $n -lt 10 ]; then n=10; fi
dd if=/dev/urandom of=$file.stuff bs=$n count=1

openssl smime -sign -inkey $file.pem -signer $file.x509 -keyform PEM \
-in $file.stuff -out $file.pkcs7 -binary -outform DER || exit $?

keyctl padd asymmetric baz @s <$file.pkcs7
done

Unfortunately, the above is somewhat limited in what it can produce.

David

2012-09-18 18:46:48

by Alan

[permalink] [raw]
Subject: Re: [PATCH 14/16] X.509: Add an ASN.1 decoder

On Tue, 18 Sep 2012 18:34:12 +0100
David Howells <[email protected]> wrote:

> Alan Cox <[email protected]> wrote:
>
> > Why do this in the kernel.That appears to be completely insane.
>
> A number of reasons:
>
> (1) The UEFI signature/key database may contain ASN.1 X.509 certificates and
> we may need to use those very early in the boot process, during initrd.

Ok that makes some sense. Presumably they've also got to fall within what
you trust and sign ?

Alan

2012-09-18 22:04:10

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 14/16] X.509: Add an ASN.1 decoder

David Howells <[email protected]> wrote:

> > has it been fuzz tested extensively ?

Also, here's a generator of random binary ASN.1. I think it should mostly
produce valid X.509, but invalid ASN.1 is okay too.

Run with something like:

while :; do ./asn1random.pl | keyctl padd asymmetric vlad @s; done

David
---
#!/usr/bin/perl -w
use strict;

my $depth = 0;
my $maxdepth = 12;
my $num_elements = 0;
my $total_elements = 30 + int(rand(400));

print STDERR "SEED: ", srand(), "\n";

###############################################################################
#
# Generate a header
#
###############################################################################
sub emit_asn1_hdr($$)
{
my ($tag, $len) = @_;
my $output = "";
my $l;

if ($len < 0x80) {
$l = $len;
} elsif ($len <= 0xff) {
$l = 0x81;
} elsif ($len <= 0xffff) {
$l = 0x82;
} elsif ($len <= 0xffffff) {
$l = 0x83;
} else {
$l = 0x84;
}

$output .= pack("CC", $tag == -1 ? int(rand(255)) & ~0x20 : $tag, $l);
if ($len < 0x80) {
} elsif ($len <= 0xff) {
$output .= pack("C", $len);
} elsif ($len <= 0xffff) {
$output .= pack("n", $len);
} elsif ($len <= 0xffffff) {
$output .= pack("Cn", $len >> 16, $len & 0xffff);
} else {
$output .= pack("N", $len);
}

return $output;
}

###############################################################################
#
# Generate a random primitive
#
###############################################################################
sub emit_asn1_prim($)
{
my ($tag) = @_;
my $output;
my $len = int(rand(255));

$len = 4; #####################

$tag = int(rand(255)) & ~0x20
if ($tag == -1);

$output = emit_asn1_hdr($tag, $len);

my $i = $len;
while ($i > 16) {
$output .= "abcdefghijklmnop";
$i -= 16;
}

$output .= substr("abcdefghijklmnop", 0, $i);
return $output;
}

###############################################################################
#
# Generate a random construct
#
###############################################################################
sub emit_asn1_cons($);
sub emit_asn1_cons($)
{
my $output = "";
my $count = int(rand(20));
my ($tag) = @_;

if ($depth >= $maxdepth) {
return emit_asn1_prim($tag);
}

if ($tag == -1) {
$tag = int(rand(255)) & ~0x20;
if ($tag < 0x40 && $tag != 0x11) {
$tag = 0x10;
}
$tag |= 0x20;
}

$depth++;
while ($count > 0) {
if (int(rand(4 + $depth)) == 1) {
$output .= emit_asn1_cons(-1);
} else {
$output .= emit_asn1_prim(-1);
}
$count--;
}
$depth--;

return emit_asn1_hdr($tag, length($output)) . $output;
}

print emit_asn1_cons(-1);

2012-09-18 22:19:06

by Peter Jones

[permalink] [raw]
Subject: Re: [PATCH 14/16] X.509: Add an ASN.1 decoder

On Tue, 2012-09-18 at 19:51 +0100, Alan Cox wrote:
> On Tue, 18 Sep 2012 18:34:12 +0100
> David Howells <[email protected]> wrote:
>
> > Alan Cox <[email protected]> wrote:
> >
> > > Why do this in the kernel.That appears to be completely insane.
> >
> > A number of reasons:
> >
> > (1) The UEFI signature/key database may contain ASN.1 X.509 certificates and
> > we may need to use those very early in the boot process, during initrd.
>
> Ok that makes some sense. Presumably they've also got to fall within what
> you trust and sign ?

The idea is that you implicitly trust keys in the lists maintained by
your system firmware and/or shim ("mok") key databases, or else you
shouldn't have Secure Boot turned on in the first place. Using these
keys and hashes allows you to e.g. relatively easily add a key you're
using to sign a module you're currently developing, while still *ahem*
enjoying the many benefits of signed modules, kernel, and bootloader.

(Though obviously we would never recommend adding a public key whose
private half you're normally keeping on that same machine.)

--
Peter


2012-09-18 22:26:22

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 14/16] X.509: Add an ASN.1 decoder

David Howells <[email protected]> wrote:

> Also, here's a generator of random binary ASN.1. I think it should mostly
> produce valid X.509, but invalid ASN.1 is okay too.
> ...
> $len = 4; #####################

With this line removed, obviously...

David

2012-09-19 04:25:59

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 14/16] X.509: Add an ASN.1 decoder

On Tue, 18 Sep 2012, Alan Cox wrote:

> On Tue, 18 Sep 2012 18:34:12 +0100
> David Howells <[email protected]> wrote:
>
> > Alan Cox <[email protected]> wrote:
> >
> > > Why do this in the kernel.That appears to be completely insane.
> >
> > A number of reasons:
> >
> > (1) The UEFI signature/key database may contain ASN.1 X.509 certificates and
> > we may need to use those very early in the boot process, during initrd.
>
> Ok that makes some sense.

I'd like to see some serious effort at code review and testing before this
code is merged.


- James
--
James Morris
<[email protected]>

2012-09-19 13:07:23

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 14/16] X.509: Add an ASN.1 decoder

David Howells <[email protected]> wrote:

> > has it been fuzz tested extensively ?

Here's a perl script to generate validly structured X.509 certificates with
random data in them. It can be run as follows:

while :; do ./x509random.pl | keyctl padd asymmetric vlad @s; done

and it can also be made to inject encoding errors:

while :; do ./x509random.pl 1 | keyctl padd asymmetric vlad @s; done

David
---
#!/usr/bin/perl -w
use strict;

print STDERR "SEED: ", srand(), "\n";

my $do_inject = ($#ARGV == 0);

my $UNIV = 0 << 6;
my $APPL = 1 << 6;
my $CONT = 2 << 6;
my $PRIV = 3 << 6;

my $BOOLEAN = 0x01;
my $INTEGER = 0x02;
my $BIT_STRING = 0x03;
my $OCTET_STRING = 0x04;
my $NULL = 0x05;
my $OBJ_ID = 0x06;
my $UTF8String = 0x0c;
my $SEQUENCE = 0x10;
my $SET = 0x11;
my $UTCTime = 0x17;
my $GeneralizedTime = 0x18;

sub maybe($)
{
return (int(rand(6)) == 0) ? '' : $_[0];
}

###############################################################################
#
# Generate a header
#
###############################################################################
sub emit_asn1_hdr($$)
{
my ($tag, $len) = @_;
my $output = "";
my $l;

if ($len < 0x80) {
$l = $len;
} elsif ($len <= 0xff) {
$l = 0x81;
} elsif ($len <= 0xffff) {
$l = 0x82;
} elsif ($len <= 0xffffff) {
$l = 0x83;
} else {
$l = 0x84;
}

$output .= pack("CC", $tag == -1 ? int(rand(255)) & ~0x20 : $tag, $l);
if ($len < 0x80) {
} elsif ($len <= 0xff) {
$output .= pack("C", $len);
} elsif ($len <= 0xffff) {
$output .= pack("n", $len);
} elsif ($len <= 0xffffff) {
$output .= pack("Cn", $len >> 16, $len & 0xffff);
} else {
$output .= pack("N", $len);
}

return $output;
}

###############################################################################
#
# Generate random data
#
###############################################################################
sub emit_random_data($$)
{
my ($minlen, $maxlen) = @_;
my $output = '';

my $len = $minlen + int(rand($maxlen - $minlen));

my $i = $len;
while ($i > 16) {
$output .= "abcdefghijklmnop";
$i -= 16;
}

$output .= substr("abcdefghijklmnop", 0, $i);
return $output;
}

###############################################################################
#
# Generate a primitive containing some random data
#
###############################################################################
sub emit_asn1_prim(@)
{
my ($class, $tag, $minlen, $maxlen) = @_;
my $content;

$minlen = 0 if (!$minlen);
$maxlen = 255 if (!$maxlen);
$content = ($tag == $NULL) ? '' : emit_random_data($minlen, $maxlen);

$tag |= $class;
return emit_asn1_hdr($tag, length($content)) . $content;
}

###############################################################################
#
# Generate an object identifier
#
###############################################################################
my %OIDs = (
commonName => pack("CCC", 85, 4, 3),
countryName => pack("CCC", 85, 4, 6),
organizationName => pack("CCC", 85, 4, 10),
organizationUnitName => pack("CCC", 85, 4, 11),
rsaEncryption => pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 1),
sha1WithRSAEncryption => pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 5),
authorityKeyIdentifier => pack("CCC", 85, 29, 35),
subjectKeyIdentifier => pack("CCC", 85, 29, 14),
basicConstraints => pack("CCC", 85, 29, 19)
);

sub emit_asn1_OID($$$)
{
my ($class, $tag, $oid_name) = @_;
my $oid;
my $len;

if (!exists($OIDs{$oid_name})) {
print STDERR "Unknown OID: $oid_name\n";
exit(2);
}

$oid = $OIDs{$oid_name};
$len = length($oid);

$tag |= $class;

return emit_asn1_hdr($tag, $len) . $oid;
}

###############################################################################
#
# Generate a UTC time
#
###############################################################################
sub emit_asn1_utctime($$)
{
my ($class, $tag) = @_;
my $output = "";
my $len;

for (my $i = 0; $i < 12; $i++) {
$output .= pack("C", int(rand(9)) + 0x30);
}
$output .= 'Z';

$len = length($output);

$tag |= $class;

return emit_asn1_hdr($tag, $len) . $output;
}

###############################################################################
#
# Generate a generalized time
#
###############################################################################
sub emit_asn1_gentime($$)
{
my ($class, $tag) = @_;
my $output = "";
my $len;

for (my $i = 0; $i < 14; $i++) {
$output .= pack("C", int(rand(9)) + 0x30);
}
$output .= 'Z';

$len = length($output);

$tag |= $class;

return emit_asn1_hdr($tag, $len) . $output;
}

###############################################################################
#
# Generate a construct
#
###############################################################################
sub emit_asn1_cons($$$)
{
my ($class, $tag, $content) = @_;
my $inject = '';

if ($do_inject) {
if (int(rand(20)) == 0) {
$inject = pack("C", int(rand(255)));
}
}

$tag |= $class | 0x20;
return emit_asn1_hdr($tag, length($content)) . $content . $inject;
}

###############################################################################
#
# Generate a name
#
###############################################################################
sub emit_x509_AttributeValueAssertion($@)
{
my ($type, $min, $max) = @_;
my $output;
$output = emit_asn1_OID($UNIV, $OBJ_ID, $type); # attributeType
$output .= emit_asn1_prim($UNIV, $UTF8String, $min, $max); # attributeValue
return emit_asn1_cons($UNIV, $SEQUENCE, $output);
}

sub emit_x509_RelativeDistinguishedName()
{
my $output;
# Set of AttributeValueAssertion
$output = emit_x509_AttributeValueAssertion("countryName", 2, 2);
$output .= emit_x509_AttributeValueAssertion("organizationName", 3, 10);
$output .= emit_x509_AttributeValueAssertion("organizationUnitName", 3, 10);
$output .= emit_x509_AttributeValueAssertion("commonName", 4, 16);
return emit_asn1_cons($UNIV, $SET, $output);
}

sub emit_x509_Name()
{
my $output;
# Sequence of RDN
$output = emit_x509_RelativeDistinguishedName();
return emit_asn1_cons($UNIV, $SEQUENCE, $output);
}

###############################################################################
#
# Generate some X.509 extensions
#
###############################################################################
sub emit_x509_SubjectKeyIdentifier()
{
my $content = emit_asn1_prim($UNIV, $OCTET_STRING, 10, 20);
return $content;
}

sub emit_x509_AuthorityKeyIdentifier()
{
my $content = emit_asn1_prim($CONT, 0, 10, 20);
my $wrapper = emit_asn1_cons($UNIV, $SEQUENCE, $content);
return $wrapper;
}

sub emit_x509_BasicConstraints()
{
my $content = emit_asn1_prim($UNIV, $BIT_STRING, 1, 7);
return $content;
}

sub emit_x509_Extension($)
{
my ($ext) = @_;
my $output;
my $value = "";

if ($ext eq "authorityKeyIdentifier") {
$output = emit_asn1_OID($UNIV, $OBJ_ID, $ext);
$value = emit_x509_AuthorityKeyIdentifier();
} elsif ($ext eq "subjectKeyIdentifier") {
$output = emit_asn1_OID($UNIV, $OBJ_ID, $ext);
$value = emit_x509_SubjectKeyIdentifier();
} elsif ($ext eq "basicConstraints") {
$output = emit_asn1_OID($UNIV, $OBJ_ID, $ext);
$value = emit_x509_BasicConstraints();
} else {
$output = emit_asn1_prim($UNIV, $OBJ_ID, 3, 10);
$value = emit_random_data(10, 20);
}

$output .= maybe emit_asn1_prim($UNIV, $BOOLEAN, 1, 1); # critical
$output .= emit_asn1_hdr($UNIV | $OCTET_STRING, length($value)) . $value;

return emit_asn1_cons($UNIV, $SEQUENCE, $output);
}

sub emit_x509_Extensions()
{
my $output = "";

# Probably do want a sequence of extensions here
$output .= maybe emit_x509_Extension("authorityKeyIdentifier");
$output .= maybe emit_x509_Extension("subjectKeyIdentifier");
$output .= maybe emit_x509_Extension("basicConstraints");
$output .= maybe emit_x509_Extension("");
$output .= maybe emit_x509_Extension("");
$output .= maybe emit_x509_Extension("");
$output .= maybe emit_x509_Extension("");

return emit_asn1_cons($CONT, 3, emit_asn1_cons($UNIV, $SEQUENCE, $output));
}

###############################################################################
#
# Generate an X.509 certificate
#
###############################################################################
sub emit_x509_Time()
{
# UTCTime or GeneralizedTime
if (int(rand(2)) == 0) {
return emit_asn1_utctime($UNIV, $UTCTime);
} else {
return emit_asn1_gentime($UNIV, $GeneralizedTime);
}
}

sub emit_x509_Validity()
{
my $output;
$output = emit_x509_Time(); # notBefore
$output .= emit_x509_Time(); # notAfter
return emit_asn1_cons($UNIV, $SEQUENCE, $output);
}

sub emit_x509_AlgorithmIdentifier($)
{
my ($oid) = @_;
my $output;

#$output = emit_asn1_prim($UNIV, $OBJ_ID); # algorithm
$output = emit_asn1_OID($UNIV, $OBJ_ID, $oid); # algorithm
$output .= emit_asn1_prim($UNIV, $NULL); # parameters
return emit_asn1_cons($UNIV, $SEQUENCE, $output);
}

sub emit_x509_Version()
{
my $output = emit_asn1_prim($UNIV, $INTEGER, 0, 3);
return emit_asn1_cons($CONT, 0, $output);
}

sub emit_x509_SubjectPublicKeyInfo()
{
my $output;
$output = emit_x509_AlgorithmIdentifier("rsaEncryption"); # algorithm
$output .= emit_asn1_prim($UNIV, $BIT_STRING); # subjectPublicKey
return emit_asn1_cons($UNIV, $SEQUENCE, $output);
}

sub emit_x509_TBSCertificate()
{
my $output;

$output = emit_x509_Version; # version
$output .= emit_asn1_prim($UNIV, $INTEGER); # serialNumber
$output .= emit_x509_AlgorithmIdentifier("sha1WithRSAEncryption"); # signature
$output .= emit_x509_Name(); # issuer
$output .= emit_x509_Validity(); # validity
$output .= emit_x509_Name(); # subject
$output .= emit_x509_SubjectPublicKeyInfo(); # subjectPublicKeyInfo
$output .= maybe emit_asn1_prim($CONT, 1); # issuerUniqueID
$output .= maybe emit_asn1_prim($CONT, 2); # subjectUniqueID
$output .= emit_x509_Extensions(); # extensions

return emit_asn1_cons($UNIV, $SEQUENCE, $output);
}

sub emit_x509_Certificate()
{
my $output;

$output = emit_x509_TBSCertificate(); # tbsCertificate
$output .= emit_x509_AlgorithmIdentifier("sha1WithRSAEncryption"); # signatureAlgorithm
$output .= emit_asn1_prim($UNIV, $BIT_STRING); # signature

return emit_asn1_cons($UNIV, $SEQUENCE, $output);
}

print emit_x509_Certificate();

2012-09-20 09:45:17

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 14/16] X.509: Add an ASN.1 decoder

James Morris <[email protected]> wrote:

> I'd like to see some serious effort at code review and testing before this
> code is merged.

With regard to testing, I've run multiple simultaneous instances of a number
of test scripts against it continuously for the best part of a day:

(1) A script to generate completely random data and attempt to stuff that
into a key. The completely random data blob is fed wholly and then
partially in decreasing amounts to keyctl padd. Then the script loops
and starts again.

./fuzz-x509.sh /tmp/data1

(2) A script to generate random valid ASN.1:

while :; do ./asn1random.pl | keyctl padd asymmetric vlad @s; done

(3) A script to generate correctly formatted X.509 certificates filled with
random data, including for the RSA key and signature fields.

while :; do ./x509random.pl | keyctl padd asymmetric vlad @s; done

(4) A variant of (3) that injects random bytes into the structure, whilst
correctly maintaining the length counts outside of those.

while :; do ./x509random.pl -i | keyctl padd asymmetric vlad @s; done

(5) A script to repeatedly generate valid X.509 certificates and stuff those
in, and then generate valid PKCS#7 signatures over random data and try to
stuff those in too (which should fail).

./x509-stuffer.sh

David


Attachments:
fuzz-x509.sh (550.00 B)
Random data stuffer script
asn1random.pl (2.30 kB)
Valid random ASN.1 generator script
x509random.pl (10.45 kB)
Validly formatted random X.509 generator script
x509-stuffer.sh (893.00 B)
Valid X.509 and PKCS7 stuffer script
Download all attachments