diff options
Diffstat (limited to 'crypto')
| -rw-r--r-- | crypto/Kconfig | 1 | ||||
| -rw-r--r-- | crypto/Makefile | 1 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/.gitignore | 1 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/Kconfig | 38 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/Makefile | 27 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/asymmetric_keys.h | 15 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/asymmetric_type.c | 274 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/public_key.c | 108 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/public_key.h | 30 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/rsa.c | 277 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/signature.c | 49 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/x509.asn1 | 60 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/x509_cert_parser.c | 496 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/x509_parser.h | 36 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/x509_public_key.c | 239 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/x509_rsakey.asn1 | 4 | ||||
| -rw-r--r-- | crypto/xor.c | 4 | 
17 files changed, 1658 insertions, 2 deletions
diff --git a/crypto/Kconfig b/crypto/Kconfig index 50402dc0ea3..6563366bae8 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -1216,5 +1216,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 a301ad2b258..8cf61ffe351 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -97,3 +97,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/.gitignore b/crypto/asymmetric_keys/.gitignore new file mode 100644 index 00000000000..ee328374dba --- /dev/null +++ b/crypto/asymmetric_keys/.gitignore @@ -0,0 +1 @@ +*-asn1.[ch] diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig new file mode 100644 index 00000000000..6d2c2ea1255 --- /dev/null +++ b/crypto/asymmetric_keys/Kconfig @@ -0,0 +1,38 @@ +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 + +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. + +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). + +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 new file mode 100644 index 00000000000..0727204aab6 --- /dev/null +++ b/crypto/asymmetric_keys/Makefile @@ -0,0 +1,27 @@ +# +# Makefile for asymmetric cryptographic keys +# + +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 + +# +# 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/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h new file mode 100644 index 00000000000..515b6343081 --- /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 (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +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 00000000000..cf807654d22 --- /dev/null +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -0,0 +1,274 @@ +/* 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 (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <keys/asymmetric-subtype.h> +#include <keys/asymmetric-parser.h> +#include <linux/seq_file.h> +#include <linux/module.h> +#include <linux/slab.h> +#include "asymmetric_keys.h" + +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: + * + *	"<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, ']'); +	} +} + +/* + * 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) +{ +	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; +} + +/* + * 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", +	.preparse	= asymmetric_key_preparse, +	.free_preparse	= asymmetric_key_free_preparse, +	.instantiate	= asymmetric_key_instantiate, +	.match		= asymmetric_key_match, +	.destroy	= asymmetric_key_destroy, +	.describe	= asymmetric_key_describe, +}; +EXPORT_SYMBOL_GPL(key_type_asymmetric); + +/** + * register_asymmetric_key_parser - Register a asymmetric key blob parser + * @parser: The parser to register + */ +int register_asymmetric_key_parser(struct asymmetric_key_parser *parser) +{ +	struct asymmetric_key_parser *cursor; +	int ret; + +	down_write(&asymmetric_key_parsers_sem); + +	list_for_each_entry(cursor, &asymmetric_key_parsers, link) { +		if (strcmp(cursor->name, parser->name) == 0) { +			pr_err("Asymmetric key parser '%s' already registered\n", +			       parser->name); +			ret = -EEXIST; +			goto out; +		} +	} + +	list_add_tail(&parser->link, &asymmetric_key_parsers); + +	pr_notice("Asymmetric key parser '%s' registered\n", parser->name); +	ret = 0; + +out: +	up_write(&asymmetric_key_parsers_sem); +	return ret; +} +EXPORT_SYMBOL_GPL(register_asymmetric_key_parser); + +/** + * unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser + * @parser: The parser to unregister + */ +void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser) +{ +	down_write(&asymmetric_key_parsers_sem); +	list_del(&parser->link); +	up_write(&asymmetric_key_parsers_sem); + +	pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name); +} +EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser); + +/* + * Module stuff + */ +static int __init asymmetric_key_init(void) +{ +	return register_key_type(&key_type_asymmetric); +} + +static void __exit asymmetric_key_cleanup(void) +{ +	unregister_key_type(&key_type_asymmetric); +} + +module_init(asymmetric_key_init); +module_exit(asymmetric_key_cleanup); diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c new file mode 100644 index 00000000000..cb2e29180a8 --- /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 (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#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 00000000000..5e5e3562689 --- /dev/null +++ b/crypto/asymmetric_keys/public_key.h @@ -0,0 +1,30 @@ +/* Public key algorithm internals + * + * See Documentation/crypto/asymmetric-keys.txt + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#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); +}; + +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 00000000000..4a6a0696f8a --- /dev/null +++ b/crypto/asymmetric_keys/rsa.c @@ -0,0 +1,277 @@ +/* RSA asymmetric public-key algorithm [RFC3447] + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#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); +	tsize = mpi_get_nbits(sig->rsa.s); + +	/* 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 (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) +		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); diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c new file mode 100644 index 00000000000..50b3f880b4f --- /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 (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#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/crypto/asymmetric_keys/x509.asn1 b/crypto/asymmetric_keys/x509.asn1 new file mode 100644 index 00000000000..bf32b3dff08 --- /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 }) +	} + +TBSCertificate ::= SEQUENCE { +	version           [ 0 ]	Version DEFAULT, +	serialNumber		CertificateSerialNumber, +	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 00000000000..7fabc4c0199 --- /dev/null +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -0,0 +1,496 @@ +/* X.509 certificate parser + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#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 */ +	u8		o_size;			/* Size of organizationName (O) */ +	u8		cn_size;		/* Size of commonName (CN) */ +	u8		email_size;		/* Size of emailAddress */ +	u16		o_offset;		/* Offset of organizationName (O) */ +	u16		cn_offset;		/* Offset of commonName (CN) */ +	u16		email_offset;		/* Offset of emailAddress */ +}; + +/* + * Free an X.509 certificate + */ +void x509_free_certificate(struct x509_certificate *cert) +{ +	if (cert) { +		public_key_destroy(cert->pub); +		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)); +		pr_debug("Unknown OID: [%lu] %s\n", +			 (unsigned long)value - ctx->data, buffer); +	} +	return 0; +} + +/* + * Save the position of the TBS data so that we can check the signature over it + * later. + */ +int x509_note_tbs_certificate(void *context, size_t hdrlen, +			      unsigned char tag, +			      const void *value, size_t vlen) +{ +	struct x509_parse_context *ctx = context; + +	pr_debug("x509_note_tbs_certificate(,%zu,%02x,%ld,%zu)!\n", +		 hdrlen, tag, (unsigned long)value - ctx->data, vlen); + +	ctx->cert->tbs = value - hdrlen; +	ctx->cert->tbs_size = vlen + hdrlen; +	return 0; +} + +/* + * Record the public key algorithm + */ +int x509_note_pkey_algo(void *context, size_t hdrlen, +			unsigned char tag, +			const void *value, size_t vlen) +{ +	struct x509_parse_context *ctx = context; + +	pr_debug("PubKey Algo: %u\n", ctx->last_oid); + +	switch (ctx->last_oid) { +	case OID_md2WithRSAEncryption: +	case OID_md3WithRSAEncryption: +	default: +		return -ENOPKG; /* Unsupported combination */ + +	case OID_md4WithRSAEncryption: +		ctx->cert->sig_hash_algo = 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("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; +} + +/* + * Note some of the name segments from which we'll fabricate a name. + */ +int x509_extract_name_segment(void *context, size_t hdrlen, +			      unsigned char tag, +			      const void *value, size_t vlen) +{ +	struct x509_parse_context *ctx = context; + +	switch (ctx->last_oid) { +	case OID_commonName: +		ctx->cn_size = vlen; +		ctx->cn_offset = (unsigned long)value - ctx->data; +		break; +	case OID_organizationName: +		ctx->o_size = vlen; +		ctx->o_offset = (unsigned long)value - ctx->data; +		break; +	case OID_email_address: +		ctx->email_size = vlen; +		ctx->email_offset = (unsigned long)value - ctx->data; +		break; +	default: +		break; +	} + +	return 0; +} + +/* + * Fabricate and save the issuer and subject names + */ +static int x509_fabricate_name(struct x509_parse_context *ctx, size_t hdrlen, +			       unsigned char tag, +			       char **_name, size_t vlen) +{ +	const void *name, *data = (const void *)ctx->data; +	size_t namesize; +	char *buffer; + +	if (*_name) +		return -EINVAL; + +	/* Empty name string if no material */ +	if (!ctx->cn_size && !ctx->o_size && !ctx->email_size) { +		buffer = kmalloc(1, GFP_KERNEL); +		if (!buffer) +			return -ENOMEM; +		buffer[0] = 0; +		goto done; +	} + +	if (ctx->cn_size && ctx->o_size) { +		/* Consider combining O and CN, but use only the CN if it is +		 * prefixed by the O, or a significant portion thereof. +		 */ +		namesize = ctx->cn_size; +		name = data + ctx->cn_offset; +		if (ctx->cn_size >= ctx->o_size && +		    memcmp(data + ctx->cn_offset, data + ctx->o_offset, +			   ctx->o_size) == 0) +			goto single_component; +		if (ctx->cn_size >= 7 && +		    ctx->o_size >= 7 && +		    memcmp(data + ctx->cn_offset, data + ctx->o_offset, 7) == 0) +			goto single_component; + +		buffer = kmalloc(ctx->o_size + 2 + ctx->cn_size + 1, +				 GFP_KERNEL); +		if (!buffer) +			return -ENOMEM; + +		memcpy(buffer, +		       data + ctx->o_offset, ctx->o_size); +		buffer[ctx->o_size + 0] = ':'; +		buffer[ctx->o_size + 1] = ' '; +		memcpy(buffer + ctx->o_size + 2, +		       data + ctx->cn_offset, ctx->cn_size); +		buffer[ctx->o_size + 2 + ctx->cn_size] = 0; +		goto done; + +	} else if (ctx->cn_size) { +		namesize = ctx->cn_size; +		name = data + ctx->cn_offset; +	} else if (ctx->o_size) { +		namesize = ctx->o_size; +		name = data + ctx->o_offset; +	} else { +		namesize = ctx->email_size; +		name = data + ctx->email_offset; +	} + +single_component: +	buffer = kmalloc(namesize + 1, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; +	memcpy(buffer, name, namesize); +	buffer[namesize] = 0; + +done: +	*_name = buffer; +	ctx->cn_size = 0; +	ctx->o_size = 0; +	ctx->email_size = 0; +	return 0; +} + +int x509_note_issuer(void *context, size_t hdrlen, +		     unsigned char tag, +		     const void *value, size_t vlen) +{ +	struct x509_parse_context *ctx = context; +	return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen); +} + +int x509_note_subject(void *context, size_t hdrlen, +		      unsigned char tag, +		      const void *value, size_t vlen) +{ +	struct x509_parse_context *ctx = context; +	return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen); +} + +/* + * 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 key MPIs 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; +		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 a certificate time. + */ +static int x509_note_time(struct tm *tm,  size_t hdrlen, +			  unsigned char tag, +			  const unsigned char *value, size_t vlen) +{ +	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; +		tm->tm_year = DD2bin(p); +		if (tm->tm_year >= 50) +			tm->tm_year += 1900; +		else +			tm->tm_year += 2000; +	} else if (tag == ASN1_GENTIM) { +		/* GenTime: YYYYMMDDHHMMSSZ */ +		if (vlen != 15) +			goto unsupported_time; +		tm->tm_year = DD2bin(p) * 100 + DD2bin(p); +	} else { +		goto unsupported_time; +	} + +	tm->tm_year -= 1900; +	tm->tm_mon  = DD2bin(p) - 1; +	tm->tm_mday = DD2bin(p); +	tm->tm_hour = DD2bin(p); +	tm->tm_min  = DD2bin(p); +	tm->tm_sec  = DD2bin(p); + +	if (*p != 'Z') +		goto unsupported_time; + +	return 0; + +unsupported_time: +	pr_debug("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 00000000000..f86dc5fcc4a --- /dev/null +++ b/crypto/asymmetric_keys/x509_parser.h @@ -0,0 +1,36 @@ +/* X.509 certificate parser internal definitions + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <crypto/public_key.h> + +struct x509_certificate { +	struct x509_certificate *next; +	struct public_key *pub;			/* Public key details */ +	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 */ +	struct tm	valid_from; +	struct tm	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 00000000000..06007f0e880 --- /dev/null +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -0,0 +1,239 @@ +/* Instantiate a public key crypto key from an X.509 Certificate + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#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_finup(desc, cert->tbs, cert->tbs_size, 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; +	struct tm 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 From: %04ld-%02d-%02d %02d:%02d:%02d\n", +		 cert->valid_from.tm_year + 1900, cert->valid_from.tm_mon + 1, +		 cert->valid_from.tm_mday, cert->valid_from.tm_hour, +		 cert->valid_from.tm_min,  cert->valid_from.tm_sec); +	pr_devel("Cert Valid To: %04ld-%02d-%02d %02d:%02d:%02d\n", +		 cert->valid_to.tm_year + 1900, cert->valid_to.tm_mon + 1, +		 cert->valid_to.tm_mday, cert->valid_to.tm_hour, +		 cert->valid_to.tm_min,  cert->valid_to.tm_sec); +	pr_devel("Cert Signature: %s + %s\n", +		 pkey_algo[cert->sig_pkey_algo], +		 pkey_hash_algo[cert->sig_hash_algo]); + +	if (!cert->fingerprint || !cert->authority) { +		pr_warn("Cert for '%s' must have SubjKeyId and AuthKeyId extensions\n", +			cert->subject); +		ret = -EKEYREJECTED; +		goto error_free_cert; +	} + +	time_to_tm(CURRENT_TIME.tv_sec, 0, &now); +	pr_devel("Now: %04ld-%02d-%02d %02d:%02d:%02d\n", +		 now.tm_year + 1900, now.tm_mon + 1, now.tm_mday, +		 now.tm_hour, now.tm_min,  now.tm_sec); +	if (now.tm_year < cert->valid_from.tm_year || +	    (now.tm_year == cert->valid_from.tm_year && +	     (now.tm_mon < cert->valid_from.tm_mon || +	      (now.tm_mon == cert->valid_from.tm_mon && +	       (now.tm_mday < cert->valid_from.tm_mday || +		(now.tm_mday == cert->valid_from.tm_mday && +		 (now.tm_hour < cert->valid_from.tm_hour || +		  (now.tm_hour == cert->valid_from.tm_hour && +		   (now.tm_min < cert->valid_from.tm_min || +		    (now.tm_min == cert->valid_from.tm_min && +		     (now.tm_sec < cert->valid_from.tm_sec +		      ))))))))))) { +		pr_warn("Cert %s is not yet valid\n", cert->fingerprint); +		ret = -EKEYREJECTED; +		goto error_free_cert; +	} +	if (now.tm_year > cert->valid_to.tm_year || +	    (now.tm_year == cert->valid_to.tm_year && +	     (now.tm_mon > cert->valid_to.tm_mon || +	      (now.tm_mon == cert->valid_to.tm_mon && +	       (now.tm_mday > cert->valid_to.tm_mday || +		(now.tm_mday == cert->valid_to.tm_mday && +		 (now.tm_hour > cert->valid_to.tm_hour || +		  (now.tm_hour == cert->valid_to.tm_hour && +		   (now.tm_min > cert->valid_to.tm_min || +		    (now.tm_min == cert->valid_to.tm_min && +		     (now.tm_sec > cert->valid_to.tm_sec +		      ))))))))))) { +		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 */ +	sulen = strlen(cert->subject); +	srlen = strlen(cert->fingerprint); +	ret = -ENOMEM; +	desc = kmalloc(sulen + 2 + srlen + 1, GFP_KERNEL); +	if (!desc) +		goto error_free_cert; +	memcpy(desc, cert->subject, sulen); +	desc[sulen] = ':'; +	desc[sulen + 1] = ' '; +	memcpy(desc + sulen + 2, cert->fingerprint, srlen); +	desc[sulen + 2 + srlen] = 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 00000000000..4ec7cc6532c --- /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 +	} diff --git a/crypto/xor.c b/crypto/xor.c index 65c7b416b4a..35d6b3adf23 100644 --- a/crypto/xor.c +++ b/crypto/xor.c @@ -56,11 +56,11 @@ xor_blocks(unsigned int src_count, unsigned int bytes, void *dest, void **srcs)  EXPORT_SYMBOL(xor_blocks);  /* Set of all registered templates.  */ -static struct xor_block_template *template_list; +static struct xor_block_template *__initdata template_list;  #define BENCH_SIZE (PAGE_SIZE) -static void +static void __init  do_xor_speed(struct xor_block_template *tmpl, void *b1, void *b2)  {  	int speed;  |