GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
menuconfig ASYMMETRIC_KEY_TYPE
|
||||
bool "Asymmetric (public-key cryptographic) key Support"
|
||||
help
|
||||
This option provides support for a key type that holds the data for
|
||||
the asymmetric keys used for public key cryptographic operations such
|
||||
as encryption, decryption, signature generation and signature
|
||||
verification.
|
||||
|
||||
if ASYMMETRIC_KEY_TYPE
|
||||
|
||||
config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
||||
bool "Asymmetric public-key crypto algorithm subtype"
|
||||
help
|
||||
This option provides support for asymmetric public key type handling.
|
||||
If signature generation and/or verification are to be used,
|
||||
appropriate hash algorithms (such as SHA-1) must be available.
|
||||
ENOPKG will be reported if the requisite algorithm is unavailable.
|
||||
|
||||
config RSA_PUBLIC_KEY_PARSER
|
||||
bool "RSA public key parser"
|
||||
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
||||
select ASN1_DECODER
|
||||
select ASN1_COMPILER
|
||||
select OID_REGISTRY
|
||||
help
|
||||
This option provides support for parsing a blob containing RSA
|
||||
public key data and provides the ability to instantiate a public
|
||||
key.
|
||||
|
||||
config X509_CERTIFICATE_PARSER
|
||||
bool "X.509 certificate parser"
|
||||
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
||||
select ASN1_DECODER
|
||||
select ASN1_COMPILER
|
||||
select OID_REGISTRY
|
||||
select LIB_DATE
|
||||
help
|
||||
This option provides support for parsing X.509 format blobs for key
|
||||
data and provides the ability to instantiate a crypto key from a
|
||||
public key packet found inside the certificate.
|
||||
|
||||
config PKCS7_MESSAGE_PARSER
|
||||
bool "PKCS#7 message parser"
|
||||
depends on X509_CERTIFICATE_PARSER
|
||||
select ASN1_DECODER
|
||||
select ASN1_COMPILER
|
||||
select OID_REGISTRY
|
||||
help
|
||||
This option provides support for parsing PKCS#7 format messages for
|
||||
signature data and provides the ability to verify the signature.
|
||||
|
||||
endif # ASYMMETRIC_KEY_TYPE
|
||||
@@ -0,0 +1,49 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Makefile for asymmetric cryptographic keys
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
|
||||
|
||||
asymmetric_keys-y := asymmetric_type.o
|
||||
|
||||
obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
|
||||
|
||||
#
|
||||
# RSA public key parser
|
||||
#
|
||||
obj-$(CONFIG_RSA_PUBLIC_KEY_PARSER) += rsa_public_key.o
|
||||
rsa_public_key-y := \
|
||||
rsapubkey.asn1.o \
|
||||
rsa_helper.o
|
||||
|
||||
$(obj)/rsapubkey.asn1.o: $(obj)/rsapubkey.asn1.c $(obj)/rsapubkey.asn1.h
|
||||
$(obj)/rsa_helper.o: $(obj)/rsapubkey.asn1.h
|
||||
|
||||
#
|
||||
# X.509 Certificate handling
|
||||
#
|
||||
obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o
|
||||
x509_key_parser-y := \
|
||||
x509.asn1.o \
|
||||
x509_akid.asn1.o \
|
||||
x509_cert_parser.o \
|
||||
x509_public_key.o
|
||||
|
||||
$(obj)/x509_cert_parser.o: \
|
||||
$(obj)/x509.asn1.h \
|
||||
$(obj)/x509_akid.asn1.h
|
||||
|
||||
$(obj)/x509.asn1.o: $(obj)/x509.asn1.c $(obj)/x509.asn1.h
|
||||
$(obj)/x509_akid.asn1.o: $(obj)/x509_akid.asn1.c $(obj)/x509_akid.asn1.h
|
||||
|
||||
#
|
||||
# PKCS#7 message handling
|
||||
#
|
||||
obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o
|
||||
pkcs7_message-y := \
|
||||
pkcs7.asn1.o \
|
||||
pkcs7_parser.o
|
||||
|
||||
$(obj)/pkcs7_parser.o: $(obj)/pkcs7.asn1.h
|
||||
$(obj)/pkcs7.asn1.o: $(obj)/pkcs7.asn1.c $(obj)/pkcs7.asn1.h
|
||||
@@ -0,0 +1,668 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/* Asymmetric public-key cryptography key type
|
||||
*
|
||||
* See Documentation/crypto/asymmetric-keys.txt
|
||||
*
|
||||
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*/
|
||||
#ifndef __UBOOT__
|
||||
#include <keys/asymmetric-subtype.h>
|
||||
#include <keys/asymmetric-parser.h>
|
||||
#endif
|
||||
#include <crypto/public_key.h>
|
||||
#ifdef __UBOOT__
|
||||
#include <linux/compat.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/string.h>
|
||||
#else
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ctype.h>
|
||||
#endif
|
||||
#ifdef __UBOOT__
|
||||
#include <keys/asymmetric-type.h>
|
||||
#else
|
||||
#include <keys/system_keyring.h>
|
||||
#include <keys/user-type.h>
|
||||
#include "asymmetric_keys.h"
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#ifndef __UBOOT__
|
||||
const char *const key_being_used_for[NR__KEY_BEING_USED_FOR] = {
|
||||
[VERIFYING_MODULE_SIGNATURE] = "mod sig",
|
||||
[VERIFYING_FIRMWARE_SIGNATURE] = "firmware sig",
|
||||
[VERIFYING_KEXEC_PE_SIGNATURE] = "kexec PE sig",
|
||||
[VERIFYING_KEY_SIGNATURE] = "key sig",
|
||||
[VERIFYING_KEY_SELF_SIGNATURE] = "key self sig",
|
||||
[VERIFYING_UNSPECIFIED_SIGNATURE] = "unspec sig",
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(key_being_used_for);
|
||||
|
||||
static LIST_HEAD(asymmetric_key_parsers);
|
||||
static DECLARE_RWSEM(asymmetric_key_parsers_sem);
|
||||
|
||||
/**
|
||||
* find_asymmetric_key - Find a key by ID.
|
||||
* @keyring: The keys to search.
|
||||
* @id_0: The first ID to look for or NULL.
|
||||
* @id_1: The second ID to look for or NULL.
|
||||
* @partial: Use partial match if true, exact if false.
|
||||
*
|
||||
* Find a key in the given keyring by identifier. The preferred identifier is
|
||||
* the id_0 and the fallback identifier is the id_1. If both are given, the
|
||||
* lookup is by the former, but the latter must also match.
|
||||
*/
|
||||
struct key *find_asymmetric_key(struct key *keyring,
|
||||
const struct asymmetric_key_id *id_0,
|
||||
const struct asymmetric_key_id *id_1,
|
||||
bool partial)
|
||||
{
|
||||
struct key *key;
|
||||
key_ref_t ref;
|
||||
const char *lookup;
|
||||
char *req, *p;
|
||||
int len;
|
||||
|
||||
BUG_ON(!id_0 && !id_1);
|
||||
|
||||
if (id_0) {
|
||||
lookup = id_0->data;
|
||||
len = id_0->len;
|
||||
} else {
|
||||
lookup = id_1->data;
|
||||
len = id_1->len;
|
||||
}
|
||||
|
||||
/* Construct an identifier "id:<keyid>". */
|
||||
p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
|
||||
if (!req)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (partial) {
|
||||
*p++ = 'i';
|
||||
*p++ = 'd';
|
||||
} else {
|
||||
*p++ = 'e';
|
||||
*p++ = 'x';
|
||||
}
|
||||
*p++ = ':';
|
||||
p = bin2hex(p, lookup, len);
|
||||
*p = 0;
|
||||
|
||||
pr_debug("Look up: \"%s\"\n", req);
|
||||
|
||||
ref = keyring_search(make_key_ref(keyring, 1),
|
||||
&key_type_asymmetric, req, true);
|
||||
if (IS_ERR(ref))
|
||||
pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref));
|
||||
kfree(req);
|
||||
|
||||
if (IS_ERR(ref)) {
|
||||
switch (PTR_ERR(ref)) {
|
||||
/* Hide some search errors */
|
||||
case -EACCES:
|
||||
case -ENOTDIR:
|
||||
case -EAGAIN:
|
||||
return ERR_PTR(-ENOKEY);
|
||||
default:
|
||||
return ERR_CAST(ref);
|
||||
}
|
||||
}
|
||||
|
||||
key = key_ref_to_ptr(ref);
|
||||
if (id_0 && id_1) {
|
||||
const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
|
||||
|
||||
if (!kids->id[1]) {
|
||||
pr_debug("First ID matches, but second is missing\n");
|
||||
goto reject;
|
||||
}
|
||||
if (!asymmetric_key_id_same(id_1, kids->id[1])) {
|
||||
pr_debug("First ID matches, but second does not\n");
|
||||
goto reject;
|
||||
}
|
||||
}
|
||||
|
||||
pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key));
|
||||
return key;
|
||||
|
||||
reject:
|
||||
key_put(key);
|
||||
return ERR_PTR(-EKEYREJECTED);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(find_asymmetric_key);
|
||||
#endif /* !__UBOOT__ */
|
||||
|
||||
/**
|
||||
* asymmetric_key_generate_id: Construct an asymmetric key ID
|
||||
* @val_1: First binary blob
|
||||
* @len_1: Length of first binary blob
|
||||
* @val_2: Second binary blob
|
||||
* @len_2: Length of second binary blob
|
||||
*
|
||||
* Construct an asymmetric key ID from a pair of binary blobs.
|
||||
*/
|
||||
struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
|
||||
size_t len_1,
|
||||
const void *val_2,
|
||||
size_t len_2)
|
||||
{
|
||||
struct asymmetric_key_id *kid;
|
||||
|
||||
kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2,
|
||||
GFP_KERNEL);
|
||||
if (!kid)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
kid->len = len_1 + len_2;
|
||||
memcpy(kid->data, val_1, len_1);
|
||||
memcpy(kid->data + len_1, val_2, len_2);
|
||||
return kid;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asymmetric_key_generate_id);
|
||||
|
||||
/**
|
||||
* asymmetric_key_id_same - Return true if two asymmetric keys IDs are the same.
|
||||
* @kid_1, @kid_2: The key IDs to compare
|
||||
*/
|
||||
bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
|
||||
const struct asymmetric_key_id *kid2)
|
||||
{
|
||||
if (!kid1 || !kid2)
|
||||
return false;
|
||||
if (kid1->len != kid2->len)
|
||||
return false;
|
||||
return memcmp(kid1->data, kid2->data, kid1->len) == 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asymmetric_key_id_same);
|
||||
|
||||
/**
|
||||
* asymmetric_key_id_partial - Return true if two asymmetric keys IDs
|
||||
* partially match
|
||||
* @kid_1, @kid_2: The key IDs to compare
|
||||
*/
|
||||
bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1,
|
||||
const struct asymmetric_key_id *kid2)
|
||||
{
|
||||
if (!kid1 || !kid2)
|
||||
return false;
|
||||
if (kid1->len < kid2->len)
|
||||
return false;
|
||||
return memcmp(kid1->data + (kid1->len - kid2->len),
|
||||
kid2->data, kid2->len) == 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asymmetric_key_id_partial);
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* asymmetric_match_key_ids - Search asymmetric key IDs
|
||||
* @kids: The list of key IDs to check
|
||||
* @match_id: The key ID we're looking for
|
||||
* @match: The match function to use
|
||||
*/
|
||||
static bool asymmetric_match_key_ids(
|
||||
const struct asymmetric_key_ids *kids,
|
||||
const struct asymmetric_key_id *match_id,
|
||||
bool (*match)(const struct asymmetric_key_id *kid1,
|
||||
const struct asymmetric_key_id *kid2))
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!kids || !match_id)
|
||||
return false;
|
||||
for (i = 0; i < ARRAY_SIZE(kids->id); i++)
|
||||
if (match(kids->id[i], match_id))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* helper function can be called directly with pre-allocated memory */
|
||||
inline int __asymmetric_key_hex_to_key_id(const char *id,
|
||||
struct asymmetric_key_id *match_id,
|
||||
size_t hexlen)
|
||||
{
|
||||
match_id->len = hexlen;
|
||||
return hex2bin(match_id->data, id, hexlen);
|
||||
}
|
||||
|
||||
/**
|
||||
* asymmetric_key_hex_to_key_id - Convert a hex string into a key ID.
|
||||
* @id: The ID as a hex string.
|
||||
*/
|
||||
struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id)
|
||||
{
|
||||
struct asymmetric_key_id *match_id;
|
||||
size_t asciihexlen;
|
||||
int ret;
|
||||
|
||||
if (!*id)
|
||||
return ERR_PTR(-EINVAL);
|
||||
asciihexlen = strlen(id);
|
||||
if (asciihexlen & 1)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
match_id = kmalloc(sizeof(struct asymmetric_key_id) + asciihexlen / 2,
|
||||
GFP_KERNEL);
|
||||
if (!match_id)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ret = __asymmetric_key_hex_to_key_id(id, match_id, asciihexlen / 2);
|
||||
if (ret < 0) {
|
||||
kfree(match_id);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
return match_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Match asymmetric keys by an exact match on an ID.
|
||||
*/
|
||||
static bool asymmetric_key_cmp(const struct key *key,
|
||||
const struct key_match_data *match_data)
|
||||
{
|
||||
const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
|
||||
const struct asymmetric_key_id *match_id = match_data->preparsed;
|
||||
|
||||
return asymmetric_match_key_ids(kids, match_id,
|
||||
asymmetric_key_id_same);
|
||||
}
|
||||
|
||||
/*
|
||||
* Match asymmetric keys by a partial match on an IDs.
|
||||
*/
|
||||
static bool asymmetric_key_cmp_partial(const struct key *key,
|
||||
const struct key_match_data *match_data)
|
||||
{
|
||||
const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
|
||||
const struct asymmetric_key_id *match_id = match_data->preparsed;
|
||||
|
||||
return asymmetric_match_key_ids(kids, match_id,
|
||||
asymmetric_key_id_partial);
|
||||
}
|
||||
|
||||
/*
|
||||
* Preparse the match criterion. If we don't set lookup_type and cmp,
|
||||
* the default will be an exact match on the key description.
|
||||
*
|
||||
* There are some specifiers for matching key IDs rather than by the key
|
||||
* description:
|
||||
*
|
||||
* "id:<id>" - find a key by partial match on any available ID
|
||||
* "ex:<id>" - find a key by exact match on any available ID
|
||||
*
|
||||
* These have to be searched by iteration rather than by direct lookup because
|
||||
* the key is hashed according to its description.
|
||||
*/
|
||||
static int asymmetric_key_match_preparse(struct key_match_data *match_data)
|
||||
{
|
||||
struct asymmetric_key_id *match_id;
|
||||
const char *spec = match_data->raw_data;
|
||||
const char *id;
|
||||
bool (*cmp)(const struct key *, const struct key_match_data *) =
|
||||
asymmetric_key_cmp;
|
||||
|
||||
if (!spec || !*spec)
|
||||
return -EINVAL;
|
||||
if (spec[0] == 'i' &&
|
||||
spec[1] == 'd' &&
|
||||
spec[2] == ':') {
|
||||
id = spec + 3;
|
||||
cmp = asymmetric_key_cmp_partial;
|
||||
} else if (spec[0] == 'e' &&
|
||||
spec[1] == 'x' &&
|
||||
spec[2] == ':') {
|
||||
id = spec + 3;
|
||||
} else {
|
||||
goto default_match;
|
||||
}
|
||||
|
||||
match_id = asymmetric_key_hex_to_key_id(id);
|
||||
if (IS_ERR(match_id))
|
||||
return PTR_ERR(match_id);
|
||||
|
||||
match_data->preparsed = match_id;
|
||||
match_data->cmp = cmp;
|
||||
match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
|
||||
return 0;
|
||||
|
||||
default_match:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free the preparsed the match criterion.
|
||||
*/
|
||||
static void asymmetric_key_match_free(struct key_match_data *match_data)
|
||||
{
|
||||
kfree(match_data->preparsed);
|
||||
}
|
||||
|
||||
/*
|
||||
* Describe the asymmetric key
|
||||
*/
|
||||
static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
|
||||
{
|
||||
const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
|
||||
const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
|
||||
const struct asymmetric_key_id *kid;
|
||||
const unsigned char *p;
|
||||
int n;
|
||||
|
||||
seq_puts(m, key->description);
|
||||
|
||||
if (subtype) {
|
||||
seq_puts(m, ": ");
|
||||
subtype->describe(key, m);
|
||||
|
||||
if (kids && kids->id[1]) {
|
||||
kid = kids->id[1];
|
||||
seq_putc(m, ' ');
|
||||
n = kid->len;
|
||||
p = kid->data;
|
||||
if (n > 4) {
|
||||
p += n - 4;
|
||||
n = 4;
|
||||
}
|
||||
seq_printf(m, "%*phN", n, p);
|
||||
}
|
||||
|
||||
seq_puts(m, " [");
|
||||
/* put something here to indicate the key's capabilities */
|
||||
seq_putc(m, ']');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Preparse a asymmetric payload to get format the contents appropriately for the
|
||||
* internal payload to cut down on the number of scans of the data performed.
|
||||
*
|
||||
* We also generate a proposed description from the contents of the key that
|
||||
* can be used to name the key if the user doesn't want to provide one.
|
||||
*/
|
||||
static int asymmetric_key_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct asymmetric_key_parser *parser;
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
if (prep->datalen == 0)
|
||||
return -EINVAL;
|
||||
|
||||
down_read(&asymmetric_key_parsers_sem);
|
||||
|
||||
ret = -EBADMSG;
|
||||
list_for_each_entry(parser, &asymmetric_key_parsers, link) {
|
||||
pr_debug("Trying parser '%s'\n", parser->name);
|
||||
|
||||
ret = parser->parse(prep);
|
||||
if (ret != -EBADMSG) {
|
||||
pr_debug("Parser recognised the format (ret %d)\n",
|
||||
ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
up_read(&asymmetric_key_parsers_sem);
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up the key ID list
|
||||
*/
|
||||
static void asymmetric_key_free_kids(struct asymmetric_key_ids *kids)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (kids) {
|
||||
for (i = 0; i < ARRAY_SIZE(kids->id); i++)
|
||||
kfree(kids->id[i]);
|
||||
kfree(kids);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up the preparse data
|
||||
*/
|
||||
static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct asymmetric_key_subtype *subtype = prep->payload.data[asym_subtype];
|
||||
struct asymmetric_key_ids *kids = prep->payload.data[asym_key_ids];
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
if (subtype) {
|
||||
subtype->destroy(prep->payload.data[asym_crypto],
|
||||
prep->payload.data[asym_auth]);
|
||||
module_put(subtype->owner);
|
||||
}
|
||||
asymmetric_key_free_kids(kids);
|
||||
kfree(prep->description);
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the data dangling from the corpse of a asymmetric key
|
||||
*/
|
||||
static void asymmetric_key_destroy(struct key *key)
|
||||
{
|
||||
struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
|
||||
struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids];
|
||||
void *data = key->payload.data[asym_crypto];
|
||||
void *auth = key->payload.data[asym_auth];
|
||||
|
||||
key->payload.data[asym_crypto] = NULL;
|
||||
key->payload.data[asym_subtype] = NULL;
|
||||
key->payload.data[asym_key_ids] = NULL;
|
||||
key->payload.data[asym_auth] = NULL;
|
||||
|
||||
if (subtype) {
|
||||
subtype->destroy(data, auth);
|
||||
module_put(subtype->owner);
|
||||
}
|
||||
|
||||
asymmetric_key_free_kids(kids);
|
||||
}
|
||||
|
||||
static struct key_restriction *asymmetric_restriction_alloc(
|
||||
key_restrict_link_func_t check,
|
||||
struct key *key)
|
||||
{
|
||||
struct key_restriction *keyres =
|
||||
kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
|
||||
|
||||
if (!keyres)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
keyres->check = check;
|
||||
keyres->key = key;
|
||||
keyres->keytype = &key_type_asymmetric;
|
||||
|
||||
return keyres;
|
||||
}
|
||||
|
||||
/*
|
||||
* look up keyring restrict functions for asymmetric keys
|
||||
*/
|
||||
static struct key_restriction *asymmetric_lookup_restriction(
|
||||
const char *restriction)
|
||||
{
|
||||
char *restrict_method;
|
||||
char *parse_buf;
|
||||
char *next;
|
||||
struct key_restriction *ret = ERR_PTR(-EINVAL);
|
||||
|
||||
if (strcmp("builtin_trusted", restriction) == 0)
|
||||
return asymmetric_restriction_alloc(
|
||||
restrict_link_by_builtin_trusted, NULL);
|
||||
|
||||
if (strcmp("builtin_and_secondary_trusted", restriction) == 0)
|
||||
return asymmetric_restriction_alloc(
|
||||
restrict_link_by_builtin_and_secondary_trusted, NULL);
|
||||
|
||||
parse_buf = kstrndup(restriction, PAGE_SIZE, GFP_KERNEL);
|
||||
if (!parse_buf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
next = parse_buf;
|
||||
restrict_method = strsep(&next, ":");
|
||||
|
||||
if ((strcmp(restrict_method, "key_or_keyring") == 0) && next) {
|
||||
char *key_text;
|
||||
key_serial_t serial;
|
||||
struct key *key;
|
||||
key_restrict_link_func_t link_fn =
|
||||
restrict_link_by_key_or_keyring;
|
||||
bool allow_null_key = false;
|
||||
|
||||
key_text = strsep(&next, ":");
|
||||
|
||||
if (next) {
|
||||
if (strcmp(next, "chain") != 0)
|
||||
goto out;
|
||||
|
||||
link_fn = restrict_link_by_key_or_keyring_chain;
|
||||
allow_null_key = true;
|
||||
}
|
||||
|
||||
if (kstrtos32(key_text, 0, &serial) < 0)
|
||||
goto out;
|
||||
|
||||
if ((serial == 0) && allow_null_key) {
|
||||
key = NULL;
|
||||
} else {
|
||||
key = key_lookup(serial);
|
||||
if (IS_ERR(key)) {
|
||||
ret = ERR_CAST(key);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = asymmetric_restriction_alloc(link_fn, key);
|
||||
if (IS_ERR(ret))
|
||||
key_put(key);
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(parse_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int asymmetric_key_eds_op(struct kernel_pkey_params *params,
|
||||
const void *in, void *out)
|
||||
{
|
||||
const struct asymmetric_key_subtype *subtype;
|
||||
struct key *key = params->key;
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
if (key->type != &key_type_asymmetric)
|
||||
return -EINVAL;
|
||||
subtype = asymmetric_key_subtype(key);
|
||||
if (!subtype ||
|
||||
!key->payload.data[0])
|
||||
return -EINVAL;
|
||||
if (!subtype->eds_op)
|
||||
return -ENOTSUPP;
|
||||
|
||||
ret = subtype->eds_op(params, in, out);
|
||||
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int asymmetric_key_verify_signature(struct kernel_pkey_params *params,
|
||||
const void *in, const void *in2)
|
||||
{
|
||||
struct public_key_signature sig = {
|
||||
.s_size = params->in2_len,
|
||||
.digest_size = params->in_len,
|
||||
.encoding = params->encoding,
|
||||
.hash_algo = params->hash_algo,
|
||||
.digest = (void *)in,
|
||||
.s = (void *)in2,
|
||||
};
|
||||
|
||||
return verify_signature(params->key, &sig);
|
||||
}
|
||||
|
||||
struct key_type key_type_asymmetric = {
|
||||
.name = "asymmetric",
|
||||
.preparse = asymmetric_key_preparse,
|
||||
.free_preparse = asymmetric_key_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match_preparse = asymmetric_key_match_preparse,
|
||||
.match_free = asymmetric_key_match_free,
|
||||
.destroy = asymmetric_key_destroy,
|
||||
.describe = asymmetric_key_describe,
|
||||
.lookup_restriction = asymmetric_lookup_restriction,
|
||||
.asym_query = query_asymmetric_key,
|
||||
.asym_eds_op = asymmetric_key_eds_op,
|
||||
.asym_verify_signature = asymmetric_key_verify_signature,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(key_type_asymmetric);
|
||||
|
||||
/**
|
||||
* register_asymmetric_key_parser - Register a asymmetric key blob parser
|
||||
* @parser: The parser to register
|
||||
*/
|
||||
int register_asymmetric_key_parser(struct asymmetric_key_parser *parser)
|
||||
{
|
||||
struct asymmetric_key_parser *cursor;
|
||||
int ret;
|
||||
|
||||
down_write(&asymmetric_key_parsers_sem);
|
||||
|
||||
list_for_each_entry(cursor, &asymmetric_key_parsers, link) {
|
||||
if (strcmp(cursor->name, parser->name) == 0) {
|
||||
pr_err("Asymmetric key parser '%s' already registered\n",
|
||||
parser->name);
|
||||
ret = -EEXIST;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
list_add_tail(&parser->link, &asymmetric_key_parsers);
|
||||
|
||||
pr_notice("Asymmetric key parser '%s' registered\n", parser->name);
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
up_write(&asymmetric_key_parsers_sem);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_asymmetric_key_parser);
|
||||
|
||||
/**
|
||||
* unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser
|
||||
* @parser: The parser to unregister
|
||||
*/
|
||||
void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser)
|
||||
{
|
||||
down_write(&asymmetric_key_parsers_sem);
|
||||
list_del(&parser->link);
|
||||
up_write(&asymmetric_key_parsers_sem);
|
||||
|
||||
pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser);
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
static int __init asymmetric_key_init(void)
|
||||
{
|
||||
return register_key_type(&key_type_asymmetric);
|
||||
}
|
||||
|
||||
static void __exit asymmetric_key_cleanup(void)
|
||||
{
|
||||
unregister_key_type(&key_type_asymmetric);
|
||||
}
|
||||
|
||||
module_init(asymmetric_key_init);
|
||||
module_exit(asymmetric_key_cleanup);
|
||||
#endif /* !__UBOOT__ */
|
||||
@@ -0,0 +1,135 @@
|
||||
PKCS7ContentInfo ::= SEQUENCE {
|
||||
contentType ContentType ({ pkcs7_check_content_type }),
|
||||
content [0] EXPLICIT SignedData OPTIONAL
|
||||
}
|
||||
|
||||
ContentType ::= OBJECT IDENTIFIER ({ pkcs7_note_OID })
|
||||
|
||||
SignedData ::= SEQUENCE {
|
||||
version INTEGER ({ pkcs7_note_signeddata_version }),
|
||||
digestAlgorithms DigestAlgorithmIdentifiers,
|
||||
contentInfo ContentInfo ({ pkcs7_note_content }),
|
||||
certificates CHOICE {
|
||||
certSet [0] IMPLICIT ExtendedCertificatesAndCertificates,
|
||||
certSequence [2] IMPLICIT Certificates
|
||||
} OPTIONAL ({ pkcs7_note_certificate_list }),
|
||||
crls CHOICE {
|
||||
crlSet [1] IMPLICIT CertificateRevocationLists,
|
||||
crlSequence [3] IMPLICIT CRLSequence
|
||||
} OPTIONAL,
|
||||
signerInfos SignerInfos
|
||||
}
|
||||
|
||||
ContentInfo ::= SEQUENCE {
|
||||
contentType ContentType ({ pkcs7_note_OID }),
|
||||
content [0] EXPLICIT Data OPTIONAL
|
||||
}
|
||||
|
||||
Data ::= ANY ({ pkcs7_note_data })
|
||||
|
||||
DigestAlgorithmIdentifiers ::= CHOICE {
|
||||
daSet SET OF DigestAlgorithmIdentifier,
|
||||
daSequence SEQUENCE OF DigestAlgorithmIdentifier
|
||||
}
|
||||
|
||||
DigestAlgorithmIdentifier ::= SEQUENCE {
|
||||
algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
parameters ANY OPTIONAL
|
||||
}
|
||||
|
||||
--
|
||||
-- Certificates and certificate lists
|
||||
--
|
||||
ExtendedCertificatesAndCertificates ::= SET OF ExtendedCertificateOrCertificate
|
||||
|
||||
ExtendedCertificateOrCertificate ::= CHOICE {
|
||||
certificate Certificate, -- X.509
|
||||
extendedCertificate [0] IMPLICIT ExtendedCertificate -- PKCS#6
|
||||
}
|
||||
|
||||
ExtendedCertificate ::= Certificate -- cheating
|
||||
|
||||
Certificates ::= SEQUENCE OF Certificate
|
||||
|
||||
CertificateRevocationLists ::= SET OF CertificateList
|
||||
|
||||
CertificateList ::= SEQUENCE OF Certificate -- This may be defined incorrectly
|
||||
|
||||
CRLSequence ::= SEQUENCE OF CertificateList
|
||||
|
||||
Certificate ::= ANY ({ pkcs7_extract_cert }) -- X.509
|
||||
|
||||
--
|
||||
-- Signer information
|
||||
--
|
||||
SignerInfos ::= CHOICE {
|
||||
siSet SET OF SignerInfo,
|
||||
siSequence SEQUENCE OF SignerInfo
|
||||
}
|
||||
|
||||
SignerInfo ::= SEQUENCE {
|
||||
version INTEGER ({ pkcs7_note_signerinfo_version }),
|
||||
sid SignerIdentifier, -- CMS variant, not PKCS#7
|
||||
digestAlgorithm DigestAlgorithmIdentifier ({ pkcs7_sig_note_digest_algo }),
|
||||
authenticatedAttributes CHOICE {
|
||||
aaSet [0] IMPLICIT SetOfAuthenticatedAttribute
|
||||
({ pkcs7_sig_note_set_of_authattrs }),
|
||||
aaSequence [2] EXPLICIT SEQUENCE OF AuthenticatedAttribute
|
||||
-- Explicit because easier to compute digest on
|
||||
-- sequence of attributes and then reuse encoded
|
||||
-- sequence in aaSequence.
|
||||
} OPTIONAL,
|
||||
digestEncryptionAlgorithm
|
||||
DigestEncryptionAlgorithmIdentifier ({ pkcs7_sig_note_pkey_algo }),
|
||||
encryptedDigest EncryptedDigest,
|
||||
unauthenticatedAttributes CHOICE {
|
||||
uaSet [1] IMPLICIT SET OF UnauthenticatedAttribute,
|
||||
uaSequence [3] IMPLICIT SEQUENCE OF UnauthenticatedAttribute
|
||||
} OPTIONAL
|
||||
} ({ pkcs7_note_signed_info })
|
||||
|
||||
SignerIdentifier ::= CHOICE {
|
||||
-- RFC5652 sec 5.3
|
||||
issuerAndSerialNumber IssuerAndSerialNumber,
|
||||
subjectKeyIdentifier [0] IMPLICIT SubjectKeyIdentifier
|
||||
}
|
||||
|
||||
IssuerAndSerialNumber ::= SEQUENCE {
|
||||
issuer Name ({ pkcs7_sig_note_issuer }),
|
||||
serialNumber CertificateSerialNumber ({ pkcs7_sig_note_serial })
|
||||
}
|
||||
|
||||
CertificateSerialNumber ::= INTEGER
|
||||
|
||||
SubjectKeyIdentifier ::= OCTET STRING ({ pkcs7_sig_note_skid })
|
||||
|
||||
SetOfAuthenticatedAttribute ::= SET OF AuthenticatedAttribute
|
||||
|
||||
AuthenticatedAttribute ::= SEQUENCE {
|
||||
type OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
values SET OF ANY ({ pkcs7_sig_note_authenticated_attr })
|
||||
}
|
||||
|
||||
UnauthenticatedAttribute ::= SEQUENCE {
|
||||
type OBJECT IDENTIFIER,
|
||||
values SET OF ANY
|
||||
}
|
||||
|
||||
DigestEncryptionAlgorithmIdentifier ::= SEQUENCE {
|
||||
algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
parameters ANY OPTIONAL
|
||||
}
|
||||
|
||||
EncryptedDigest ::= OCTET STRING ({ pkcs7_sig_note_signature })
|
||||
|
||||
---
|
||||
--- X.500 Name
|
||||
---
|
||||
Name ::= SEQUENCE OF RelativeDistinguishedName
|
||||
|
||||
RelativeDistinguishedName ::= SET OF AttributeValueAssertion
|
||||
|
||||
AttributeValueAssertion ::= SEQUENCE {
|
||||
attributeType OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
attributeValue ANY
|
||||
}
|
||||
@@ -0,0 +1,693 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/* PKCS#7 parser
|
||||
*
|
||||
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "PKCS7: "fmt
|
||||
#ifdef __UBOOT__
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/compat.h>
|
||||
#endif
|
||||
#include <linux/kernel.h>
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/module.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#endif
|
||||
#include <linux/err.h>
|
||||
#include <linux/oid_registry.h>
|
||||
#include <crypto/public_key.h>
|
||||
#include "pkcs7_parser.h"
|
||||
#include "pkcs7.asn1.h"
|
||||
|
||||
MODULE_DESCRIPTION("PKCS#7 parser");
|
||||
MODULE_AUTHOR("Red Hat, Inc.");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct pkcs7_parse_context {
|
||||
struct pkcs7_message *msg; /* Message being constructed */
|
||||
struct pkcs7_signed_info *sinfo; /* SignedInfo being constructed */
|
||||
struct pkcs7_signed_info **ppsinfo;
|
||||
struct x509_certificate *certs; /* Certificate cache */
|
||||
struct x509_certificate **ppcerts;
|
||||
unsigned long data; /* Start of data */
|
||||
enum OID last_oid; /* Last OID encountered */
|
||||
unsigned x509_index;
|
||||
unsigned sinfo_index;
|
||||
const void *raw_serial;
|
||||
unsigned raw_serial_size;
|
||||
unsigned raw_issuer_size;
|
||||
const void *raw_issuer;
|
||||
const void *raw_skid;
|
||||
unsigned raw_skid_size;
|
||||
bool expect_skid;
|
||||
};
|
||||
|
||||
/*
|
||||
* Free a signed information block.
|
||||
*/
|
||||
static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)
|
||||
{
|
||||
if (sinfo) {
|
||||
public_key_signature_free(sinfo->sig);
|
||||
kfree(sinfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pkcs7_free_message - Free a PKCS#7 message
|
||||
* @pkcs7: The PKCS#7 message to free
|
||||
*/
|
||||
void pkcs7_free_message(struct pkcs7_message *pkcs7)
|
||||
{
|
||||
struct x509_certificate *cert;
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
|
||||
if (pkcs7) {
|
||||
while (pkcs7->certs) {
|
||||
cert = pkcs7->certs;
|
||||
pkcs7->certs = cert->next;
|
||||
x509_free_certificate(cert);
|
||||
}
|
||||
while (pkcs7->crl) {
|
||||
cert = pkcs7->crl;
|
||||
pkcs7->crl = cert->next;
|
||||
x509_free_certificate(cert);
|
||||
}
|
||||
while (pkcs7->signed_infos) {
|
||||
sinfo = pkcs7->signed_infos;
|
||||
pkcs7->signed_infos = sinfo->next;
|
||||
pkcs7_free_signed_info(sinfo);
|
||||
}
|
||||
kfree(pkcs7);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_free_message);
|
||||
|
||||
/*
|
||||
* Check authenticatedAttributes are provided or not provided consistently.
|
||||
*/
|
||||
static int pkcs7_check_authattrs(struct pkcs7_message *msg)
|
||||
{
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
bool want = false;
|
||||
|
||||
sinfo = msg->signed_infos;
|
||||
if (!sinfo)
|
||||
goto inconsistent;
|
||||
|
||||
if (sinfo->authattrs) {
|
||||
want = true;
|
||||
msg->have_authattrs = true;
|
||||
}
|
||||
|
||||
for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next)
|
||||
if (!!sinfo->authattrs != want)
|
||||
goto inconsistent;
|
||||
return 0;
|
||||
|
||||
inconsistent:
|
||||
pr_warn("Inconsistently supplied authAttrs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* pkcs7_parse_message - Parse a PKCS#7 message
|
||||
* @data: The raw binary ASN.1 encoded message to be parsed
|
||||
* @datalen: The size of the encoded message
|
||||
*/
|
||||
struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx;
|
||||
struct pkcs7_message *msg = ERR_PTR(-ENOMEM);
|
||||
int ret;
|
||||
|
||||
ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
goto out_no_ctx;
|
||||
ctx->msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);
|
||||
if (!ctx->msg)
|
||||
goto out_no_msg;
|
||||
ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
|
||||
if (!ctx->sinfo)
|
||||
goto out_no_sinfo;
|
||||
ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
|
||||
GFP_KERNEL);
|
||||
if (!ctx->sinfo->sig)
|
||||
goto out_no_sig;
|
||||
|
||||
ctx->data = (unsigned long)data;
|
||||
ctx->ppcerts = &ctx->certs;
|
||||
ctx->ppsinfo = &ctx->msg->signed_infos;
|
||||
|
||||
/* Attempt to decode the signature */
|
||||
ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);
|
||||
if (ret < 0) {
|
||||
msg = ERR_PTR(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = pkcs7_check_authattrs(ctx->msg);
|
||||
if (ret < 0) {
|
||||
msg = ERR_PTR(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
msg = ctx->msg;
|
||||
ctx->msg = NULL;
|
||||
|
||||
out:
|
||||
while (ctx->certs) {
|
||||
struct x509_certificate *cert = ctx->certs;
|
||||
ctx->certs = cert->next;
|
||||
x509_free_certificate(cert);
|
||||
}
|
||||
out_no_sig:
|
||||
pkcs7_free_signed_info(ctx->sinfo);
|
||||
out_no_sinfo:
|
||||
pkcs7_free_message(ctx->msg);
|
||||
out_no_msg:
|
||||
kfree(ctx);
|
||||
out_no_ctx:
|
||||
return msg;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_parse_message);
|
||||
|
||||
/**
|
||||
* pkcs7_get_content_data - Get access to the PKCS#7 content
|
||||
* @pkcs7: The preparsed PKCS#7 message to access
|
||||
* @_data: Place to return a pointer to the data
|
||||
* @_data_len: Place to return the data length
|
||||
* @_headerlen: Size of ASN.1 header not included in _data
|
||||
*
|
||||
* Get access to the data content of the PKCS#7 message. The size of the
|
||||
* header of the ASN.1 object that contains it is also provided and can be used
|
||||
* to adjust *_data and *_data_len to get the entire object.
|
||||
*
|
||||
* Returns -ENODATA if the data object was missing from the message.
|
||||
*/
|
||||
int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
|
||||
const void **_data, size_t *_data_len,
|
||||
size_t *_headerlen)
|
||||
{
|
||||
if (!pkcs7->data)
|
||||
return -ENODATA;
|
||||
|
||||
*_data = pkcs7->data;
|
||||
*_data_len = pkcs7->data_len;
|
||||
if (_headerlen)
|
||||
*_headerlen = pkcs7->data_hdrlen;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_get_content_data);
|
||||
|
||||
/*
|
||||
* Note an OID when we find one for later processing when we know how
|
||||
* to interpret it.
|
||||
*/
|
||||
int pkcs7_note_OID(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
ctx->last_oid = look_up_OID(value, vlen);
|
||||
if (ctx->last_oid == OID__NR) {
|
||||
char buffer[50];
|
||||
sprint_oid(value, vlen, buffer, sizeof(buffer));
|
||||
printk("PKCS7: Unknown OID: [%lu] %s\n",
|
||||
(unsigned long)value - ctx->data, buffer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the digest algorithm for the signature.
|
||||
*/
|
||||
int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
switch (ctx->last_oid) {
|
||||
case OID_md4:
|
||||
ctx->sinfo->sig->hash_algo = "md4";
|
||||
break;
|
||||
case OID_md5:
|
||||
ctx->sinfo->sig->hash_algo = "md5";
|
||||
break;
|
||||
case OID_sha1:
|
||||
ctx->sinfo->sig->hash_algo = "sha1";
|
||||
break;
|
||||
case OID_sha256:
|
||||
ctx->sinfo->sig->hash_algo = "sha256";
|
||||
break;
|
||||
case OID_sha384:
|
||||
ctx->sinfo->sig->hash_algo = "sha384";
|
||||
break;
|
||||
case OID_sha512:
|
||||
ctx->sinfo->sig->hash_algo = "sha512";
|
||||
break;
|
||||
case OID_sha224:
|
||||
ctx->sinfo->sig->hash_algo = "sha224";
|
||||
break;
|
||||
default:
|
||||
printk("Unsupported digest algo: %u\n", ctx->last_oid);
|
||||
return -ENOPKG;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the public key algorithm for the signature.
|
||||
*/
|
||||
int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
switch (ctx->last_oid) {
|
||||
case OID_rsaEncryption:
|
||||
ctx->sinfo->sig->pkey_algo = "rsa";
|
||||
ctx->sinfo->sig->encoding = "pkcs1";
|
||||
break;
|
||||
default:
|
||||
printk("Unsupported pkey algo: %u\n", ctx->last_oid);
|
||||
return -ENOPKG;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only support signed data [RFC2315 sec 9].
|
||||
*/
|
||||
int pkcs7_check_content_type(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
if (ctx->last_oid != OID_signed_data) {
|
||||
pr_warn("Only support pkcs7_signedData type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the SignedData version
|
||||
*/
|
||||
int pkcs7_note_signeddata_version(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
unsigned version;
|
||||
|
||||
if (vlen != 1)
|
||||
goto unsupported;
|
||||
|
||||
ctx->msg->version = version = *(const u8 *)value;
|
||||
switch (version) {
|
||||
case 1:
|
||||
/* PKCS#7 SignedData [RFC2315 sec 9.1]
|
||||
* CMS ver 1 SignedData [RFC5652 sec 5.1]
|
||||
*/
|
||||
break;
|
||||
case 3:
|
||||
/* CMS ver 3 SignedData [RFC2315 sec 5.1] */
|
||||
break;
|
||||
default:
|
||||
goto unsupported;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unsupported:
|
||||
pr_warn("Unsupported SignedData version\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the SignerInfo version
|
||||
*/
|
||||
int pkcs7_note_signerinfo_version(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
unsigned version;
|
||||
|
||||
if (vlen != 1)
|
||||
goto unsupported;
|
||||
|
||||
version = *(const u8 *)value;
|
||||
switch (version) {
|
||||
case 1:
|
||||
/* PKCS#7 SignerInfo [RFC2315 sec 9.2]
|
||||
* CMS ver 1 SignerInfo [RFC5652 sec 5.3]
|
||||
*/
|
||||
if (ctx->msg->version != 1)
|
||||
goto version_mismatch;
|
||||
ctx->expect_skid = false;
|
||||
break;
|
||||
case 3:
|
||||
/* CMS ver 3 SignerInfo [RFC2315 sec 5.3] */
|
||||
if (ctx->msg->version == 1)
|
||||
goto version_mismatch;
|
||||
ctx->expect_skid = true;
|
||||
break;
|
||||
default:
|
||||
goto unsupported;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unsupported:
|
||||
pr_warn("Unsupported SignerInfo version\n");
|
||||
return -EINVAL;
|
||||
version_mismatch:
|
||||
pr_warn("SignedData-SignerInfo version mismatch\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a certificate and store it in the context.
|
||||
*/
|
||||
int pkcs7_extract_cert(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
struct x509_certificate *x509;
|
||||
|
||||
if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) {
|
||||
pr_debug("Cert began with tag %02x at %lu\n",
|
||||
tag, (unsigned long)ctx - ctx->data);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* We have to correct for the header so that the X.509 parser can start
|
||||
* from the beginning. Note that since X.509 stipulates DER, there
|
||||
* probably shouldn't be an EOC trailer - but it is in PKCS#7 (which
|
||||
* stipulates BER).
|
||||
*/
|
||||
value -= hdrlen;
|
||||
vlen += hdrlen;
|
||||
|
||||
if (((u8*)value)[1] == 0x80)
|
||||
vlen += 2; /* Indefinite length - there should be an EOC */
|
||||
|
||||
x509 = x509_cert_parse(value, vlen);
|
||||
if (IS_ERR(x509))
|
||||
return PTR_ERR(x509);
|
||||
|
||||
x509->index = ++ctx->x509_index;
|
||||
pr_debug("Got cert %u for %s\n", x509->index, x509->subject);
|
||||
pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data);
|
||||
|
||||
*ctx->ppcerts = x509;
|
||||
ctx->ppcerts = &x509->next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the certificate list
|
||||
*/
|
||||
int pkcs7_note_certificate_list(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
pr_devel("Got cert list (%02x)\n", tag);
|
||||
|
||||
*ctx->ppcerts = ctx->msg->certs;
|
||||
ctx->msg->certs = ctx->certs;
|
||||
ctx->certs = NULL;
|
||||
ctx->ppcerts = &ctx->certs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the content type.
|
||||
*/
|
||||
int pkcs7_note_content(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
if (ctx->last_oid != OID_data &&
|
||||
ctx->last_oid != OID_msIndirectData) {
|
||||
pr_warn("Unsupported data type %d\n", ctx->last_oid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctx->msg->data_type = ctx->last_oid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the data from the message and store that and its content type OID in
|
||||
* the context.
|
||||
*/
|
||||
int pkcs7_note_data(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
pr_debug("Got data\n");
|
||||
|
||||
ctx->msg->data = value;
|
||||
ctx->msg->data_len = vlen;
|
||||
ctx->msg->data_hdrlen = hdrlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse authenticated attributes.
|
||||
*/
|
||||
int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
struct pkcs7_signed_info *sinfo = ctx->sinfo;
|
||||
enum OID content_type;
|
||||
|
||||
pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
|
||||
|
||||
switch (ctx->last_oid) {
|
||||
case OID_contentType:
|
||||
if (__test_and_set_bit(sinfo_has_content_type, &sinfo->aa_set))
|
||||
goto repeated;
|
||||
content_type = look_up_OID(value, vlen);
|
||||
if (content_type != ctx->msg->data_type) {
|
||||
pr_warn("Mismatch between global data type (%d) and sinfo %u (%d)\n",
|
||||
ctx->msg->data_type, sinfo->index,
|
||||
content_type);
|
||||
return -EBADMSG;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case OID_signingTime:
|
||||
if (__test_and_set_bit(sinfo_has_signing_time, &sinfo->aa_set))
|
||||
goto repeated;
|
||||
/* Should we check that the signing time is consistent
|
||||
* with the signer's X.509 cert?
|
||||
*/
|
||||
return x509_decode_time(&sinfo->signing_time,
|
||||
hdrlen, tag, value, vlen);
|
||||
|
||||
case OID_messageDigest:
|
||||
if (__test_and_set_bit(sinfo_has_message_digest, &sinfo->aa_set))
|
||||
goto repeated;
|
||||
if (tag != ASN1_OTS)
|
||||
return -EBADMSG;
|
||||
sinfo->msgdigest = value;
|
||||
sinfo->msgdigest_len = vlen;
|
||||
return 0;
|
||||
|
||||
case OID_smimeCapabilites:
|
||||
if (__test_and_set_bit(sinfo_has_smime_caps, &sinfo->aa_set))
|
||||
goto repeated;
|
||||
#ifdef __UBOOT__ /* OID_data is needed for authenticated UEFI variables */
|
||||
if (ctx->msg->data_type != OID_msIndirectData &&
|
||||
ctx->msg->data_type != OID_data) {
|
||||
#else
|
||||
if (ctx->msg->data_type != OID_msIndirectData) {
|
||||
#endif
|
||||
pr_warn("S/MIME Caps only allowed with Authenticode\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
return 0;
|
||||
|
||||
/* Microsoft SpOpusInfo seems to be contain cont[0] 16-bit BE
|
||||
* char URLs and cont[1] 8-bit char URLs.
|
||||
*
|
||||
* Microsoft StatementType seems to contain a list of OIDs that
|
||||
* are also used as extendedKeyUsage types in X.509 certs.
|
||||
*/
|
||||
case OID_msSpOpusInfo:
|
||||
if (__test_and_set_bit(sinfo_has_ms_opus_info, &sinfo->aa_set))
|
||||
goto repeated;
|
||||
goto authenticode_check;
|
||||
case OID_msStatementType:
|
||||
if (__test_and_set_bit(sinfo_has_ms_statement_type, &sinfo->aa_set))
|
||||
goto repeated;
|
||||
authenticode_check:
|
||||
if (ctx->msg->data_type != OID_msIndirectData) {
|
||||
pr_warn("Authenticode AuthAttrs only allowed with Authenticode\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
/* I'm not sure how to validate these */
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
repeated:
|
||||
/* We permit max one item per AuthenticatedAttribute and no repeats */
|
||||
pr_warn("Repeated/multivalue AuthAttrs not permitted\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the set of auth attributes for digestion purposes [RFC2315 sec 9.3]
|
||||
*/
|
||||
int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
struct pkcs7_signed_info *sinfo = ctx->sinfo;
|
||||
|
||||
if (!test_bit(sinfo_has_content_type, &sinfo->aa_set) ||
|
||||
!test_bit(sinfo_has_message_digest, &sinfo->aa_set)) {
|
||||
pr_warn("Missing required AuthAttr\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (ctx->msg->data_type != OID_msIndirectData &&
|
||||
test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) {
|
||||
pr_warn("Unexpected Authenticode AuthAttr\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* We need to switch the 'CONT 0' to a 'SET OF' when we digest */
|
||||
sinfo->authattrs = value - (hdrlen - 1);
|
||||
sinfo->authattrs_len = vlen + (hdrlen - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the issuing certificate serial number
|
||||
*/
|
||||
int pkcs7_sig_note_serial(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
ctx->raw_serial = value;
|
||||
ctx->raw_serial_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the issuer's name
|
||||
*/
|
||||
int pkcs7_sig_note_issuer(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
ctx->raw_issuer = value;
|
||||
ctx->raw_issuer_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the issuing cert's subjectKeyIdentifier
|
||||
*/
|
||||
int pkcs7_sig_note_skid(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
pr_devel("SKID: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
|
||||
|
||||
ctx->raw_skid = value;
|
||||
ctx->raw_skid_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the signature data
|
||||
*/
|
||||
int pkcs7_sig_note_signature(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL);
|
||||
if (!ctx->sinfo->sig->s)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->sinfo->sig->s_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note a signature information block
|
||||
*/
|
||||
int pkcs7_note_signed_info(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
struct pkcs7_signed_info *sinfo = ctx->sinfo;
|
||||
struct asymmetric_key_id *kid;
|
||||
|
||||
if (ctx->msg->data_type == OID_msIndirectData && !sinfo->authattrs) {
|
||||
pr_warn("Authenticode requires AuthAttrs\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* Generate cert issuer + serial number key ID */
|
||||
if (!ctx->expect_skid) {
|
||||
kid = asymmetric_key_generate_id(ctx->raw_serial,
|
||||
ctx->raw_serial_size,
|
||||
ctx->raw_issuer,
|
||||
ctx->raw_issuer_size);
|
||||
} else {
|
||||
kid = asymmetric_key_generate_id(ctx->raw_skid,
|
||||
ctx->raw_skid_size,
|
||||
"", 0);
|
||||
}
|
||||
if (IS_ERR(kid))
|
||||
return PTR_ERR(kid);
|
||||
|
||||
pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);
|
||||
|
||||
sinfo->sig->auth_ids[0] = kid;
|
||||
sinfo->index = ++ctx->sinfo_index;
|
||||
*ctx->ppsinfo = sinfo;
|
||||
ctx->ppsinfo = &sinfo->next;
|
||||
ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
|
||||
if (!ctx->sinfo)
|
||||
return -ENOMEM;
|
||||
ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
|
||||
GFP_KERNEL);
|
||||
if (!ctx->sinfo->sig)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/* PKCS#7 crypto data parser internal definitions
|
||||
*
|
||||
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*/
|
||||
|
||||
#include <linux/oid_registry.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include "x509_parser.h"
|
||||
|
||||
#define kenter(FMT, ...) \
|
||||
pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
|
||||
#define kleave(FMT, ...) \
|
||||
pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
|
||||
|
||||
struct pkcs7_signed_info {
|
||||
struct pkcs7_signed_info *next;
|
||||
struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
|
||||
unsigned index;
|
||||
bool unsupported_crypto; /* T if not usable due to missing crypto */
|
||||
bool blacklisted;
|
||||
|
||||
/* Message digest - the digest of the Content Data (or NULL) */
|
||||
const void *msgdigest;
|
||||
unsigned msgdigest_len;
|
||||
|
||||
/* Authenticated Attribute data (or NULL) */
|
||||
unsigned authattrs_len;
|
||||
const void *authattrs;
|
||||
unsigned long aa_set;
|
||||
#define sinfo_has_content_type 0
|
||||
#define sinfo_has_signing_time 1
|
||||
#define sinfo_has_message_digest 2
|
||||
#define sinfo_has_smime_caps 3
|
||||
#define sinfo_has_ms_opus_info 4
|
||||
#define sinfo_has_ms_statement_type 5
|
||||
time64_t signing_time;
|
||||
|
||||
/* Message signature.
|
||||
*
|
||||
* This contains the generated digest of _either_ the Content Data or
|
||||
* the Authenticated Attributes [RFC2315 9.3]. If the latter, one of
|
||||
* the attributes contains the digest of the the Content Data within
|
||||
* it.
|
||||
*
|
||||
* THis also contains the issuing cert serial number and issuer's name
|
||||
* [PKCS#7 or CMS ver 1] or issuing cert's SKID [CMS ver 3].
|
||||
*/
|
||||
struct public_key_signature *sig;
|
||||
};
|
||||
|
||||
struct pkcs7_message {
|
||||
struct x509_certificate *certs; /* Certificate list */
|
||||
struct x509_certificate *crl; /* Revocation list */
|
||||
struct pkcs7_signed_info *signed_infos;
|
||||
u8 version; /* Version of cert (1 -> PKCS#7 or CMS; 3 -> CMS) */
|
||||
bool have_authattrs; /* T if have authattrs */
|
||||
|
||||
/* Content Data (or NULL) */
|
||||
enum OID data_type; /* Type of Data */
|
||||
size_t data_len; /* Length of Data */
|
||||
size_t data_hdrlen; /* Length of Data ASN.1 header */
|
||||
const void *data; /* Content Data (or 0) */
|
||||
};
|
||||
@@ -0,0 +1,376 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/* In-software asymmetric public-key crypto subtype
|
||||
*
|
||||
* See Documentation/crypto/asymmetric-keys.txt
|
||||
*
|
||||
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "PKEY: "fmt
|
||||
#ifdef __UBOOT__
|
||||
#include <linux/compat.h>
|
||||
#else
|
||||
#include <linux/module.h>
|
||||
#include <linux/export.h>
|
||||
#endif
|
||||
#include <linux/kernel.h>
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/slab.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <keys/asymmetric-subtype.h>
|
||||
#endif
|
||||
#include <crypto/public_key.h>
|
||||
#ifndef __UBOOT__
|
||||
#include <crypto/akcipher.h>
|
||||
#endif
|
||||
|
||||
MODULE_DESCRIPTION("In-software asymmetric public-key subtype");
|
||||
MODULE_AUTHOR("Red Hat, Inc.");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/*
|
||||
* Provide a part of a description of the key for /proc/keys.
|
||||
*/
|
||||
static void public_key_describe(const struct key *asymmetric_key,
|
||||
struct seq_file *m)
|
||||
{
|
||||
struct public_key *key = asymmetric_key->payload.data[asym_crypto];
|
||||
|
||||
if (key)
|
||||
seq_printf(m, "%s.%s", key->id_type, key->pkey_algo);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Destroy a public key algorithm key.
|
||||
*/
|
||||
void public_key_free(struct public_key *key)
|
||||
{
|
||||
if (key) {
|
||||
kfree(key->key);
|
||||
kfree(key->params);
|
||||
kfree(key);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(public_key_free);
|
||||
|
||||
#ifdef __UBOOT__
|
||||
/*
|
||||
* from <linux>/crypto/asymmetric_keys/signature.c
|
||||
*
|
||||
* Destroy a public key signature.
|
||||
*/
|
||||
void public_key_signature_free(struct public_key_signature *sig)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (sig) {
|
||||
for (i = 0; i < ARRAY_SIZE(sig->auth_ids); i++)
|
||||
free(sig->auth_ids[i]);
|
||||
free(sig->s);
|
||||
free(sig->digest);
|
||||
free(sig);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(public_key_signature_free);
|
||||
|
||||
#else
|
||||
/*
|
||||
* Destroy a public key algorithm key.
|
||||
*/
|
||||
static void public_key_destroy(void *payload0, void *payload3)
|
||||
{
|
||||
public_key_free(payload0);
|
||||
public_key_signature_free(payload3);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the crypto algorithm name.
|
||||
*/
|
||||
static
|
||||
int software_key_determine_akcipher(const char *encoding,
|
||||
const char *hash_algo,
|
||||
const struct public_key *pkey,
|
||||
char alg_name[CRYPTO_MAX_ALG_NAME])
|
||||
{
|
||||
int n;
|
||||
|
||||
if (strcmp(encoding, "pkcs1") == 0) {
|
||||
/* The data wangled by the RSA algorithm is typically padded
|
||||
* and encoded in some manner, such as EMSA-PKCS1-1_5 [RFC3447
|
||||
* sec 8.2].
|
||||
*/
|
||||
if (!hash_algo)
|
||||
n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME,
|
||||
"pkcs1pad(%s)",
|
||||
pkey->pkey_algo);
|
||||
else
|
||||
n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME,
|
||||
"pkcs1pad(%s,%s)",
|
||||
pkey->pkey_algo, hash_algo);
|
||||
return n >= CRYPTO_MAX_ALG_NAME ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
if (strcmp(encoding, "raw") == 0) {
|
||||
strcpy(alg_name, pkey->pkey_algo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOPKG;
|
||||
}
|
||||
|
||||
static u8 *pkey_pack_u32(u8 *dst, u32 val)
|
||||
{
|
||||
memcpy(dst, &val, sizeof(val));
|
||||
return dst + sizeof(val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Query information about a key.
|
||||
*/
|
||||
static int software_key_query(const struct kernel_pkey_params *params,
|
||||
struct kernel_pkey_query *info)
|
||||
{
|
||||
struct crypto_akcipher *tfm;
|
||||
struct public_key *pkey = params->key->payload.data[asym_crypto];
|
||||
char alg_name[CRYPTO_MAX_ALG_NAME];
|
||||
u8 *key, *ptr;
|
||||
int ret, len;
|
||||
|
||||
ret = software_key_determine_akcipher(params->encoding,
|
||||
params->hash_algo,
|
||||
pkey, alg_name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tfm = crypto_alloc_akcipher(alg_name, 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return PTR_ERR(tfm);
|
||||
|
||||
key = kmalloc(pkey->keylen + sizeof(u32) * 2 + pkey->paramlen,
|
||||
GFP_KERNEL);
|
||||
if (!key)
|
||||
goto error_free_tfm;
|
||||
memcpy(key, pkey->key, pkey->keylen);
|
||||
ptr = key + pkey->keylen;
|
||||
ptr = pkey_pack_u32(ptr, pkey->algo);
|
||||
ptr = pkey_pack_u32(ptr, pkey->paramlen);
|
||||
memcpy(ptr, pkey->params, pkey->paramlen);
|
||||
|
||||
if (pkey->key_is_private)
|
||||
ret = crypto_akcipher_set_priv_key(tfm, key, pkey->keylen);
|
||||
else
|
||||
ret = crypto_akcipher_set_pub_key(tfm, key, pkey->keylen);
|
||||
if (ret < 0)
|
||||
goto error_free_key;
|
||||
|
||||
len = crypto_akcipher_maxsize(tfm);
|
||||
info->key_size = len * 8;
|
||||
info->max_data_size = len;
|
||||
info->max_sig_size = len;
|
||||
info->max_enc_size = len;
|
||||
info->max_dec_size = len;
|
||||
info->supported_ops = (KEYCTL_SUPPORTS_ENCRYPT |
|
||||
KEYCTL_SUPPORTS_VERIFY);
|
||||
if (pkey->key_is_private)
|
||||
info->supported_ops |= (KEYCTL_SUPPORTS_DECRYPT |
|
||||
KEYCTL_SUPPORTS_SIGN);
|
||||
ret = 0;
|
||||
|
||||
error_free_key:
|
||||
kfree(key);
|
||||
error_free_tfm:
|
||||
crypto_free_akcipher(tfm);
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do encryption, decryption and signing ops.
|
||||
*/
|
||||
static int software_key_eds_op(struct kernel_pkey_params *params,
|
||||
const void *in, void *out)
|
||||
{
|
||||
const struct public_key *pkey = params->key->payload.data[asym_crypto];
|
||||
struct akcipher_request *req;
|
||||
struct crypto_akcipher *tfm;
|
||||
struct crypto_wait cwait;
|
||||
struct scatterlist in_sg, out_sg;
|
||||
char alg_name[CRYPTO_MAX_ALG_NAME];
|
||||
char *key, *ptr;
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
ret = software_key_determine_akcipher(params->encoding,
|
||||
params->hash_algo,
|
||||
pkey, alg_name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tfm = crypto_alloc_akcipher(alg_name, 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return PTR_ERR(tfm);
|
||||
|
||||
req = akcipher_request_alloc(tfm, GFP_KERNEL);
|
||||
if (!req)
|
||||
goto error_free_tfm;
|
||||
|
||||
key = kmalloc(pkey->keylen + sizeof(u32) * 2 + pkey->paramlen,
|
||||
GFP_KERNEL);
|
||||
if (!key)
|
||||
goto error_free_req;
|
||||
|
||||
memcpy(key, pkey->key, pkey->keylen);
|
||||
ptr = key + pkey->keylen;
|
||||
ptr = pkey_pack_u32(ptr, pkey->algo);
|
||||
ptr = pkey_pack_u32(ptr, pkey->paramlen);
|
||||
memcpy(ptr, pkey->params, pkey->paramlen);
|
||||
|
||||
if (pkey->key_is_private)
|
||||
ret = crypto_akcipher_set_priv_key(tfm, key, pkey->keylen);
|
||||
else
|
||||
ret = crypto_akcipher_set_pub_key(tfm, key, pkey->keylen);
|
||||
if (ret)
|
||||
goto error_free_key;
|
||||
|
||||
sg_init_one(&in_sg, in, params->in_len);
|
||||
sg_init_one(&out_sg, out, params->out_len);
|
||||
akcipher_request_set_crypt(req, &in_sg, &out_sg, params->in_len,
|
||||
params->out_len);
|
||||
crypto_init_wait(&cwait);
|
||||
akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
|
||||
CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
crypto_req_done, &cwait);
|
||||
|
||||
/* Perform the encryption calculation. */
|
||||
switch (params->op) {
|
||||
case kernel_pkey_encrypt:
|
||||
ret = crypto_akcipher_encrypt(req);
|
||||
break;
|
||||
case kernel_pkey_decrypt:
|
||||
ret = crypto_akcipher_decrypt(req);
|
||||
break;
|
||||
case kernel_pkey_sign:
|
||||
ret = crypto_akcipher_sign(req);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
ret = crypto_wait_req(ret, &cwait);
|
||||
if (ret == 0)
|
||||
ret = req->dst_len;
|
||||
|
||||
error_free_key:
|
||||
kfree(key);
|
||||
error_free_req:
|
||||
akcipher_request_free(req);
|
||||
error_free_tfm:
|
||||
crypto_free_akcipher(tfm);
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify a signature using a public key.
|
||||
*/
|
||||
int public_key_verify_signature(const struct public_key *pkey,
|
||||
const struct public_key_signature *sig)
|
||||
{
|
||||
struct crypto_wait cwait;
|
||||
struct crypto_akcipher *tfm;
|
||||
struct akcipher_request *req;
|
||||
struct scatterlist src_sg[2];
|
||||
char alg_name[CRYPTO_MAX_ALG_NAME];
|
||||
char *key, *ptr;
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
BUG_ON(!pkey);
|
||||
BUG_ON(!sig);
|
||||
BUG_ON(!sig->s);
|
||||
|
||||
ret = software_key_determine_akcipher(sig->encoding,
|
||||
sig->hash_algo,
|
||||
pkey, alg_name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tfm = crypto_alloc_akcipher(alg_name, 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return PTR_ERR(tfm);
|
||||
|
||||
ret = -ENOMEM;
|
||||
req = akcipher_request_alloc(tfm, GFP_KERNEL);
|
||||
if (!req)
|
||||
goto error_free_tfm;
|
||||
|
||||
key = kmalloc(pkey->keylen + sizeof(u32) * 2 + pkey->paramlen,
|
||||
GFP_KERNEL);
|
||||
if (!key)
|
||||
goto error_free_req;
|
||||
|
||||
memcpy(key, pkey->key, pkey->keylen);
|
||||
ptr = key + pkey->keylen;
|
||||
ptr = pkey_pack_u32(ptr, pkey->algo);
|
||||
ptr = pkey_pack_u32(ptr, pkey->paramlen);
|
||||
memcpy(ptr, pkey->params, pkey->paramlen);
|
||||
|
||||
if (pkey->key_is_private)
|
||||
ret = crypto_akcipher_set_priv_key(tfm, key, pkey->keylen);
|
||||
else
|
||||
ret = crypto_akcipher_set_pub_key(tfm, key, pkey->keylen);
|
||||
if (ret)
|
||||
goto error_free_key;
|
||||
|
||||
sg_init_table(src_sg, 2);
|
||||
sg_set_buf(&src_sg[0], sig->s, sig->s_size);
|
||||
sg_set_buf(&src_sg[1], sig->digest, sig->digest_size);
|
||||
akcipher_request_set_crypt(req, src_sg, NULL, sig->s_size,
|
||||
sig->digest_size);
|
||||
crypto_init_wait(&cwait);
|
||||
akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
|
||||
CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
crypto_req_done, &cwait);
|
||||
ret = crypto_wait_req(crypto_akcipher_verify(req), &cwait);
|
||||
|
||||
error_free_key:
|
||||
kfree(key);
|
||||
error_free_req:
|
||||
akcipher_request_free(req);
|
||||
error_free_tfm:
|
||||
crypto_free_akcipher(tfm);
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
if (WARN_ON_ONCE(ret > 0))
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(public_key_verify_signature);
|
||||
|
||||
static int public_key_verify_signature_2(const struct key *key,
|
||||
const struct public_key_signature *sig)
|
||||
{
|
||||
const struct public_key *pk = key->payload.data[asym_crypto];
|
||||
return public_key_verify_signature(pk, sig);
|
||||
}
|
||||
|
||||
/*
|
||||
* Public key algorithm asymmetric key subtype
|
||||
*/
|
||||
struct asymmetric_key_subtype public_key_subtype = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "public_key",
|
||||
.name_len = sizeof("public_key") - 1,
|
||||
.describe = public_key_describe,
|
||||
.destroy = public_key_destroy,
|
||||
.query = software_key_query,
|
||||
.eds_op = software_key_eds_op,
|
||||
.verify_signature = public_key_verify_signature_2,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(public_key_subtype);
|
||||
#endif /* !__UBOOT__ */
|
||||
@@ -0,0 +1,198 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* RSA key extract helper
|
||||
*
|
||||
* Copyright (c) 2015, Intel Corporation
|
||||
* Authors: Tadeusz Struk <tadeusz.struk@intel.com>
|
||||
*/
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#endif
|
||||
#include <linux/err.h>
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/fips.h>
|
||||
#endif
|
||||
#include <crypto/internal/rsa.h>
|
||||
#include "rsapubkey.asn1.h"
|
||||
#ifndef __UBOOT__
|
||||
#include "rsaprivkey.asn1.h"
|
||||
#endif
|
||||
|
||||
int rsa_get_n(void *context, size_t hdrlen, unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct rsa_key *key = context;
|
||||
#ifndef __UBOOT__
|
||||
const u8 *ptr = value;
|
||||
size_t n_sz = vlen;
|
||||
#endif
|
||||
|
||||
/* invalid key provided */
|
||||
if (!value || !vlen)
|
||||
return -EINVAL;
|
||||
|
||||
#ifndef __UBOOT__
|
||||
if (fips_enabled) {
|
||||
while (n_sz && !*ptr) {
|
||||
ptr++;
|
||||
n_sz--;
|
||||
}
|
||||
|
||||
/* In FIPS mode only allow key size 2K and higher */
|
||||
if (n_sz < 256) {
|
||||
pr_err("RSA: key size not allowed in FIPS mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
key->n = value;
|
||||
key->n_sz = vlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsa_get_e(void *context, size_t hdrlen, unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct rsa_key *key = context;
|
||||
|
||||
/* invalid key provided */
|
||||
if (!value || !key->n_sz || !vlen || vlen > key->n_sz)
|
||||
return -EINVAL;
|
||||
|
||||
key->e = value;
|
||||
key->e_sz = vlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsa_get_d(void *context, size_t hdrlen, unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct rsa_key *key = context;
|
||||
|
||||
/* invalid key provided */
|
||||
if (!value || !key->n_sz || !vlen || vlen > key->n_sz)
|
||||
return -EINVAL;
|
||||
|
||||
key->d = value;
|
||||
key->d_sz = vlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsa_get_p(void *context, size_t hdrlen, unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct rsa_key *key = context;
|
||||
|
||||
/* invalid key provided */
|
||||
if (!value || !vlen || vlen > key->n_sz)
|
||||
return -EINVAL;
|
||||
|
||||
key->p = value;
|
||||
key->p_sz = vlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsa_get_q(void *context, size_t hdrlen, unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct rsa_key *key = context;
|
||||
|
||||
/* invalid key provided */
|
||||
if (!value || !vlen || vlen > key->n_sz)
|
||||
return -EINVAL;
|
||||
|
||||
key->q = value;
|
||||
key->q_sz = vlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsa_get_dp(void *context, size_t hdrlen, unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct rsa_key *key = context;
|
||||
|
||||
/* invalid key provided */
|
||||
if (!value || !vlen || vlen > key->n_sz)
|
||||
return -EINVAL;
|
||||
|
||||
key->dp = value;
|
||||
key->dp_sz = vlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsa_get_dq(void *context, size_t hdrlen, unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct rsa_key *key = context;
|
||||
|
||||
/* invalid key provided */
|
||||
if (!value || !vlen || vlen > key->n_sz)
|
||||
return -EINVAL;
|
||||
|
||||
key->dq = value;
|
||||
key->dq_sz = vlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsa_get_qinv(void *context, size_t hdrlen, unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct rsa_key *key = context;
|
||||
|
||||
/* invalid key provided */
|
||||
if (!value || !vlen || vlen > key->n_sz)
|
||||
return -EINVAL;
|
||||
|
||||
key->qinv = value;
|
||||
key->qinv_sz = vlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rsa_parse_pub_key() - decodes the BER encoded buffer and stores in the
|
||||
* provided struct rsa_key, pointers to the raw key as is,
|
||||
* so that the caller can copy it or MPI parse it, etc.
|
||||
*
|
||||
* @rsa_key: struct rsa_key key representation
|
||||
* @key: key in BER format
|
||||
* @key_len: length of key
|
||||
*
|
||||
* Return: 0 on success or error code in case of error
|
||||
*/
|
||||
int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key,
|
||||
unsigned int key_len)
|
||||
{
|
||||
return asn1_ber_decoder(&rsapubkey_decoder, rsa_key, key, key_len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rsa_parse_pub_key);
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* rsa_parse_priv_key() - decodes the BER encoded buffer and stores in the
|
||||
* provided struct rsa_key, pointers to the raw key
|
||||
* as is, so that the caller can copy it or MPI parse it,
|
||||
* etc.
|
||||
*
|
||||
* @rsa_key: struct rsa_key key representation
|
||||
* @key: key in BER format
|
||||
* @key_len: length of key
|
||||
*
|
||||
* Return: 0 on success or error code in case of error
|
||||
*/
|
||||
int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key,
|
||||
unsigned int key_len)
|
||||
{
|
||||
return asn1_ber_decoder(&rsaprivkey_decoder, rsa_key, key, key_len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rsa_parse_priv_key);
|
||||
#endif
|
||||
@@ -0,0 +1,4 @@
|
||||
RsaPubKey ::= SEQUENCE {
|
||||
n INTEGER ({ rsa_get_n }),
|
||||
e INTEGER ({ rsa_get_e })
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
Certificate ::= SEQUENCE {
|
||||
tbsCertificate TBSCertificate ({ x509_note_tbs_certificate }),
|
||||
signatureAlgorithm AlgorithmIdentifier,
|
||||
signature BIT STRING ({ x509_note_signature })
|
||||
}
|
||||
|
||||
TBSCertificate ::= SEQUENCE {
|
||||
version [ 0 ] Version DEFAULT,
|
||||
serialNumber CertificateSerialNumber ({ x509_note_serial }),
|
||||
signature AlgorithmIdentifier ({ x509_note_pkey_algo }),
|
||||
issuer Name ({ x509_note_issuer }),
|
||||
validity Validity,
|
||||
subject Name ({ x509_note_subject }),
|
||||
subjectPublicKeyInfo SubjectPublicKeyInfo,
|
||||
issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
|
||||
subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
|
||||
extensions [ 3 ] Extensions OPTIONAL
|
||||
}
|
||||
|
||||
Version ::= INTEGER
|
||||
CertificateSerialNumber ::= INTEGER
|
||||
|
||||
AlgorithmIdentifier ::= SEQUENCE {
|
||||
algorithm OBJECT IDENTIFIER ({ x509_note_OID }),
|
||||
parameters ANY OPTIONAL ({ x509_note_params })
|
||||
}
|
||||
|
||||
Name ::= SEQUENCE OF RelativeDistinguishedName
|
||||
|
||||
RelativeDistinguishedName ::= SET OF AttributeValueAssertion
|
||||
|
||||
AttributeValueAssertion ::= SEQUENCE {
|
||||
attributeType OBJECT IDENTIFIER ({ x509_note_OID }),
|
||||
attributeValue ANY ({ x509_extract_name_segment })
|
||||
}
|
||||
|
||||
Validity ::= SEQUENCE {
|
||||
notBefore Time ({ x509_note_not_before }),
|
||||
notAfter Time ({ x509_note_not_after })
|
||||
}
|
||||
|
||||
Time ::= CHOICE {
|
||||
utcTime UTCTime,
|
||||
generalTime GeneralizedTime
|
||||
}
|
||||
|
||||
SubjectPublicKeyInfo ::= SEQUENCE {
|
||||
algorithm AlgorithmIdentifier,
|
||||
subjectPublicKey BIT STRING ({ x509_extract_key_data })
|
||||
}
|
||||
|
||||
UniqueIdentifier ::= BIT STRING
|
||||
|
||||
Extensions ::= SEQUENCE OF Extension
|
||||
|
||||
Extension ::= SEQUENCE {
|
||||
extnid OBJECT IDENTIFIER ({ x509_note_OID }),
|
||||
critical BOOLEAN DEFAULT,
|
||||
extnValue OCTET STRING ({ x509_process_extension })
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
-- X.509 AuthorityKeyIdentifier
|
||||
-- rfc5280 section 4.2.1.1
|
||||
|
||||
AuthorityKeyIdentifier ::= SEQUENCE {
|
||||
keyIdentifier [0] IMPLICIT KeyIdentifier OPTIONAL,
|
||||
authorityCertIssuer [1] IMPLICIT GeneralNames OPTIONAL,
|
||||
authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL
|
||||
}
|
||||
|
||||
KeyIdentifier ::= OCTET STRING ({ x509_akid_note_kid })
|
||||
|
||||
CertificateSerialNumber ::= INTEGER ({ x509_akid_note_serial })
|
||||
|
||||
GeneralNames ::= SEQUENCE OF GeneralName
|
||||
|
||||
GeneralName ::= CHOICE {
|
||||
otherName [0] ANY,
|
||||
rfc822Name [1] IA5String,
|
||||
dNSName [2] IA5String,
|
||||
x400Address [3] ANY,
|
||||
directoryName [4] Name ({ x509_akid_note_name }),
|
||||
ediPartyName [5] ANY,
|
||||
uniformResourceIdentifier [6] IA5String,
|
||||
iPAddress [7] OCTET STRING,
|
||||
registeredID [8] OBJECT IDENTIFIER
|
||||
}
|
||||
|
||||
Name ::= SEQUENCE OF RelativeDistinguishedName
|
||||
|
||||
RelativeDistinguishedName ::= SET OF AttributeValueAssertion
|
||||
|
||||
AttributeValueAssertion ::= SEQUENCE {
|
||||
attributeType OBJECT IDENTIFIER ({ x509_note_OID }),
|
||||
attributeValue ANY ({ x509_extract_name_segment })
|
||||
}
|
||||
@@ -0,0 +1,697 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/* X.509 certificate parser
|
||||
*
|
||||
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "X.509: "fmt
|
||||
#include <linux/kernel.h>
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#endif
|
||||
#include <linux/err.h>
|
||||
#include <linux/oid_registry.h>
|
||||
#ifdef __UBOOT__
|
||||
#include <linux/string.h>
|
||||
#endif
|
||||
#include <crypto/public_key.h>
|
||||
#include "x509_parser.h"
|
||||
#include "x509.asn1.h"
|
||||
#include "x509_akid.asn1.h"
|
||||
|
||||
struct x509_parse_context {
|
||||
struct x509_certificate *cert; /* Certificate being constructed */
|
||||
unsigned long data; /* Start of data */
|
||||
const void *cert_start; /* Start of cert content */
|
||||
const void *key; /* Key data */
|
||||
size_t key_size; /* Size of key data */
|
||||
const void *params; /* Key parameters */
|
||||
size_t params_size; /* Size of key parameters */
|
||||
enum OID key_algo; /* Public key algorithm */
|
||||
enum OID last_oid; /* Last OID encountered */
|
||||
enum OID algo_oid; /* Algorithm OID */
|
||||
unsigned char nr_mpi; /* Number of MPIs stored */
|
||||
u8 o_size; /* Size of organizationName (O) */
|
||||
u8 cn_size; /* Size of commonName (CN) */
|
||||
u8 email_size; /* Size of emailAddress */
|
||||
u16 o_offset; /* Offset of organizationName (O) */
|
||||
u16 cn_offset; /* Offset of commonName (CN) */
|
||||
u16 email_offset; /* Offset of emailAddress */
|
||||
unsigned raw_akid_size;
|
||||
const void *raw_akid; /* Raw authorityKeyId in ASN.1 */
|
||||
const void *akid_raw_issuer; /* Raw directoryName in authorityKeyId */
|
||||
unsigned akid_raw_issuer_size;
|
||||
};
|
||||
|
||||
/*
|
||||
* Free an X.509 certificate
|
||||
*/
|
||||
void x509_free_certificate(struct x509_certificate *cert)
|
||||
{
|
||||
if (cert) {
|
||||
public_key_free(cert->pub);
|
||||
public_key_signature_free(cert->sig);
|
||||
kfree(cert->issuer);
|
||||
kfree(cert->subject);
|
||||
kfree(cert->id);
|
||||
kfree(cert->skid);
|
||||
kfree(cert);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(x509_free_certificate);
|
||||
|
||||
/*
|
||||
* Parse an X.509 certificate
|
||||
*/
|
||||
struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
|
||||
{
|
||||
struct x509_certificate *cert;
|
||||
struct x509_parse_context *ctx;
|
||||
struct asymmetric_key_id *kid;
|
||||
long ret;
|
||||
|
||||
ret = -ENOMEM;
|
||||
cert = kzalloc(sizeof(struct x509_certificate), GFP_KERNEL);
|
||||
if (!cert)
|
||||
goto error_no_cert;
|
||||
cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
|
||||
if (!cert->pub)
|
||||
goto error_no_ctx;
|
||||
cert->sig = kzalloc(sizeof(struct public_key_signature), GFP_KERNEL);
|
||||
if (!cert->sig)
|
||||
goto error_no_ctx;
|
||||
ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
goto error_no_ctx;
|
||||
|
||||
ctx->cert = cert;
|
||||
ctx->data = (unsigned long)data;
|
||||
|
||||
/* Attempt to decode the certificate */
|
||||
ret = asn1_ber_decoder(&x509_decoder, ctx, data, datalen);
|
||||
if (ret < 0)
|
||||
goto error_decode;
|
||||
|
||||
/* Decode the AuthorityKeyIdentifier */
|
||||
if (ctx->raw_akid) {
|
||||
pr_devel("AKID: %u %*phN\n",
|
||||
ctx->raw_akid_size, ctx->raw_akid_size, ctx->raw_akid);
|
||||
ret = asn1_ber_decoder(&x509_akid_decoder, ctx,
|
||||
ctx->raw_akid, ctx->raw_akid_size);
|
||||
if (ret < 0) {
|
||||
pr_warn("Couldn't decode AuthKeyIdentifier\n");
|
||||
goto error_decode;
|
||||
}
|
||||
}
|
||||
|
||||
ret = -ENOMEM;
|
||||
cert->pub->key = kmemdup(ctx->key, ctx->key_size, GFP_KERNEL);
|
||||
if (!cert->pub->key)
|
||||
goto error_decode;
|
||||
|
||||
cert->pub->keylen = ctx->key_size;
|
||||
|
||||
cert->pub->params = kmemdup(ctx->params, ctx->params_size, GFP_KERNEL);
|
||||
if (!cert->pub->params)
|
||||
goto error_decode;
|
||||
|
||||
cert->pub->paramlen = ctx->params_size;
|
||||
cert->pub->algo = ctx->key_algo;
|
||||
|
||||
/* Grab the signature bits */
|
||||
ret = x509_get_sig_params(cert);
|
||||
if (ret < 0)
|
||||
goto error_decode;
|
||||
|
||||
/* Generate cert issuer + serial number key ID */
|
||||
kid = asymmetric_key_generate_id(cert->raw_serial,
|
||||
cert->raw_serial_size,
|
||||
cert->raw_issuer,
|
||||
cert->raw_issuer_size);
|
||||
if (IS_ERR(kid)) {
|
||||
ret = PTR_ERR(kid);
|
||||
goto error_decode;
|
||||
}
|
||||
cert->id = kid;
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/* Detect self-signed certificates */
|
||||
ret = x509_check_for_self_signed(cert);
|
||||
if (ret < 0)
|
||||
goto error_decode;
|
||||
#endif
|
||||
|
||||
kfree(ctx);
|
||||
return cert;
|
||||
|
||||
error_decode:
|
||||
kfree(ctx);
|
||||
error_no_ctx:
|
||||
x509_free_certificate(cert);
|
||||
error_no_cert:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(x509_cert_parse);
|
||||
|
||||
/*
|
||||
* Note an OID when we find one for later processing when we know how
|
||||
* to interpret it.
|
||||
*/
|
||||
int x509_note_OID(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
|
||||
ctx->last_oid = look_up_OID(value, vlen);
|
||||
if (ctx->last_oid == OID__NR) {
|
||||
char buffer[50];
|
||||
sprint_oid(value, vlen, buffer, sizeof(buffer));
|
||||
pr_debug("Unknown OID: [%lu] %s\n",
|
||||
(unsigned long)value - ctx->data, buffer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the position of the TBS data so that we can check the signature over it
|
||||
* later.
|
||||
*/
|
||||
int x509_note_tbs_certificate(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
|
||||
pr_debug("x509_note_tbs_certificate(,%zu,%02x,%ld,%zu)!\n",
|
||||
hdrlen, tag, (unsigned long)value - ctx->data, vlen);
|
||||
|
||||
ctx->cert->tbs = value - hdrlen;
|
||||
ctx->cert->tbs_size = vlen + hdrlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Record the public key algorithm
|
||||
*/
|
||||
int x509_note_pkey_algo(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
|
||||
pr_debug("PubKey Algo: %u\n", ctx->last_oid);
|
||||
|
||||
switch (ctx->last_oid) {
|
||||
case OID_md2WithRSAEncryption:
|
||||
case OID_md3WithRSAEncryption:
|
||||
default:
|
||||
return -ENOPKG; /* Unsupported combination */
|
||||
|
||||
case OID_md4WithRSAEncryption:
|
||||
ctx->cert->sig->hash_algo = "md4";
|
||||
goto rsa_pkcs1;
|
||||
|
||||
case OID_sha1WithRSAEncryption:
|
||||
ctx->cert->sig->hash_algo = "sha1";
|
||||
goto rsa_pkcs1;
|
||||
|
||||
case OID_sha256WithRSAEncryption:
|
||||
ctx->cert->sig->hash_algo = "sha256";
|
||||
goto rsa_pkcs1;
|
||||
|
||||
case OID_sha384WithRSAEncryption:
|
||||
ctx->cert->sig->hash_algo = "sha384";
|
||||
goto rsa_pkcs1;
|
||||
|
||||
case OID_sha512WithRSAEncryption:
|
||||
ctx->cert->sig->hash_algo = "sha512";
|
||||
goto rsa_pkcs1;
|
||||
|
||||
case OID_sha224WithRSAEncryption:
|
||||
ctx->cert->sig->hash_algo = "sha224";
|
||||
goto rsa_pkcs1;
|
||||
|
||||
case OID_gost2012Signature256:
|
||||
ctx->cert->sig->hash_algo = "streebog256";
|
||||
goto ecrdsa;
|
||||
|
||||
case OID_gost2012Signature512:
|
||||
ctx->cert->sig->hash_algo = "streebog512";
|
||||
goto ecrdsa;
|
||||
}
|
||||
|
||||
rsa_pkcs1:
|
||||
ctx->cert->sig->pkey_algo = "rsa";
|
||||
ctx->cert->sig->encoding = "pkcs1";
|
||||
ctx->algo_oid = ctx->last_oid;
|
||||
return 0;
|
||||
ecrdsa:
|
||||
ctx->cert->sig->pkey_algo = "ecrdsa";
|
||||
ctx->cert->sig->encoding = "raw";
|
||||
ctx->algo_oid = ctx->last_oid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the whereabouts and type of the signature.
|
||||
*/
|
||||
int x509_note_signature(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
|
||||
pr_debug("Signature type: %u size %zu\n", ctx->last_oid, vlen);
|
||||
|
||||
if (ctx->last_oid != ctx->algo_oid) {
|
||||
pr_warn("Got cert with pkey (%u) and sig (%u) algorithm OIDs\n",
|
||||
ctx->algo_oid, ctx->last_oid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strcmp(ctx->cert->sig->pkey_algo, "rsa") == 0 ||
|
||||
strcmp(ctx->cert->sig->pkey_algo, "ecrdsa") == 0) {
|
||||
/* Discard the BIT STRING metadata */
|
||||
if (vlen < 1 || *(const u8 *)value != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
value++;
|
||||
vlen--;
|
||||
}
|
||||
|
||||
ctx->cert->raw_sig = value;
|
||||
ctx->cert->raw_sig_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the certificate serial number
|
||||
*/
|
||||
int x509_note_serial(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
ctx->cert->raw_serial = value;
|
||||
ctx->cert->raw_serial_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note some of the name segments from which we'll fabricate a name.
|
||||
*/
|
||||
int x509_extract_name_segment(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
|
||||
switch (ctx->last_oid) {
|
||||
case OID_commonName:
|
||||
ctx->cn_size = vlen;
|
||||
ctx->cn_offset = (unsigned long)value - ctx->data;
|
||||
break;
|
||||
case OID_organizationName:
|
||||
ctx->o_size = vlen;
|
||||
ctx->o_offset = (unsigned long)value - ctx->data;
|
||||
break;
|
||||
case OID_email_address:
|
||||
ctx->email_size = vlen;
|
||||
ctx->email_offset = (unsigned long)value - ctx->data;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fabricate and save the issuer and subject names
|
||||
*/
|
||||
static int x509_fabricate_name(struct x509_parse_context *ctx, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
char **_name, size_t vlen)
|
||||
{
|
||||
const void *name, *data = (const void *)ctx->data;
|
||||
size_t namesize;
|
||||
char *buffer;
|
||||
|
||||
if (*_name)
|
||||
return -EINVAL;
|
||||
|
||||
/* Empty name string if no material */
|
||||
if (!ctx->cn_size && !ctx->o_size && !ctx->email_size) {
|
||||
buffer = kmalloc(1, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
buffer[0] = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ctx->cn_size && ctx->o_size) {
|
||||
/* Consider combining O and CN, but use only the CN if it is
|
||||
* prefixed by the O, or a significant portion thereof.
|
||||
*/
|
||||
namesize = ctx->cn_size;
|
||||
name = data + ctx->cn_offset;
|
||||
if (ctx->cn_size >= ctx->o_size &&
|
||||
memcmp(data + ctx->cn_offset, data + ctx->o_offset,
|
||||
ctx->o_size) == 0)
|
||||
goto single_component;
|
||||
if (ctx->cn_size >= 7 &&
|
||||
ctx->o_size >= 7 &&
|
||||
memcmp(data + ctx->cn_offset, data + ctx->o_offset, 7) == 0)
|
||||
goto single_component;
|
||||
|
||||
buffer = kmalloc(ctx->o_size + 2 + ctx->cn_size + 1,
|
||||
GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buffer,
|
||||
data + ctx->o_offset, ctx->o_size);
|
||||
buffer[ctx->o_size + 0] = ':';
|
||||
buffer[ctx->o_size + 1] = ' ';
|
||||
memcpy(buffer + ctx->o_size + 2,
|
||||
data + ctx->cn_offset, ctx->cn_size);
|
||||
buffer[ctx->o_size + 2 + ctx->cn_size] = 0;
|
||||
goto done;
|
||||
|
||||
} else if (ctx->cn_size) {
|
||||
namesize = ctx->cn_size;
|
||||
name = data + ctx->cn_offset;
|
||||
} else if (ctx->o_size) {
|
||||
namesize = ctx->o_size;
|
||||
name = data + ctx->o_offset;
|
||||
} else {
|
||||
namesize = ctx->email_size;
|
||||
name = data + ctx->email_offset;
|
||||
}
|
||||
|
||||
single_component:
|
||||
buffer = kmalloc(namesize + 1, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
memcpy(buffer, name, namesize);
|
||||
buffer[namesize] = 0;
|
||||
|
||||
done:
|
||||
*_name = buffer;
|
||||
ctx->cn_size = 0;
|
||||
ctx->o_size = 0;
|
||||
ctx->email_size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int x509_note_issuer(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
ctx->cert->raw_issuer = value;
|
||||
ctx->cert->raw_issuer_size = vlen;
|
||||
return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen);
|
||||
}
|
||||
|
||||
int x509_note_subject(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
ctx->cert->raw_subject = value;
|
||||
ctx->cert->raw_subject_size = vlen;
|
||||
return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the parameters for the public key
|
||||
*/
|
||||
int x509_note_params(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
|
||||
/*
|
||||
* AlgorithmIdentifier is used three times in the x509, we should skip
|
||||
* first and ignore third, using second one which is after subject and
|
||||
* before subjectPublicKey.
|
||||
*/
|
||||
if (!ctx->cert->raw_subject || ctx->key)
|
||||
return 0;
|
||||
ctx->params = value - hdrlen;
|
||||
ctx->params_size = vlen + hdrlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the data for the public key algorithm
|
||||
*/
|
||||
int x509_extract_key_data(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
|
||||
ctx->key_algo = ctx->last_oid;
|
||||
if (ctx->last_oid == OID_rsaEncryption)
|
||||
ctx->cert->pub->pkey_algo = "rsa";
|
||||
else if (ctx->last_oid == OID_gost2012PKey256 ||
|
||||
ctx->last_oid == OID_gost2012PKey512)
|
||||
ctx->cert->pub->pkey_algo = "ecrdsa";
|
||||
else
|
||||
return -ENOPKG;
|
||||
|
||||
/* Discard the BIT STRING metadata */
|
||||
if (vlen < 1 || *(const u8 *)value != 0)
|
||||
return -EBADMSG;
|
||||
ctx->key = value + 1;
|
||||
ctx->key_size = vlen - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The keyIdentifier in AuthorityKeyIdentifier SEQUENCE is tag(CONT,PRIM,0) */
|
||||
#define SEQ_TAG_KEYID (ASN1_CONT << 6)
|
||||
|
||||
/*
|
||||
* Process certificate extensions that are used to qualify the certificate.
|
||||
*/
|
||||
int x509_process_extension(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
struct asymmetric_key_id *kid;
|
||||
const unsigned char *v = value;
|
||||
|
||||
pr_debug("Extension: %u\n", ctx->last_oid);
|
||||
|
||||
if (ctx->last_oid == OID_subjectKeyIdentifier) {
|
||||
/* Get hold of the key fingerprint */
|
||||
if (ctx->cert->skid || vlen < 3)
|
||||
return -EBADMSG;
|
||||
if (v[0] != ASN1_OTS || v[1] != vlen - 2)
|
||||
return -EBADMSG;
|
||||
v += 2;
|
||||
vlen -= 2;
|
||||
|
||||
ctx->cert->raw_skid_size = vlen;
|
||||
ctx->cert->raw_skid = v;
|
||||
kid = asymmetric_key_generate_id(v, vlen, "", 0);
|
||||
if (IS_ERR(kid))
|
||||
return PTR_ERR(kid);
|
||||
ctx->cert->skid = kid;
|
||||
pr_debug("subjkeyid %*phN\n", kid->len, kid->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ctx->last_oid == OID_authorityKeyIdentifier) {
|
||||
/* Get hold of the CA key fingerprint */
|
||||
ctx->raw_akid = v;
|
||||
ctx->raw_akid_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* x509_decode_time - Decode an X.509 time ASN.1 object
|
||||
* @_t: The time to fill in
|
||||
* @hdrlen: The length of the object header
|
||||
* @tag: The object tag
|
||||
* @value: The object value
|
||||
* @vlen: The size of the object value
|
||||
*
|
||||
* Decode an ASN.1 universal time or generalised time field into a struct the
|
||||
* kernel can handle and check it for validity. The time is decoded thus:
|
||||
*
|
||||
* [RFC5280 §4.1.2.5]
|
||||
* CAs conforming to this profile MUST always encode certificate validity
|
||||
* dates through the year 2049 as UTCTime; certificate validity dates in
|
||||
* 2050 or later MUST be encoded as GeneralizedTime. Conforming
|
||||
* applications MUST be able to process validity dates that are encoded in
|
||||
* either UTCTime or GeneralizedTime.
|
||||
*/
|
||||
int x509_decode_time(time64_t *_t, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const unsigned char *value, size_t vlen)
|
||||
{
|
||||
static const unsigned char month_lengths[] = { 31, 28, 31, 30, 31, 30,
|
||||
31, 31, 30, 31, 30, 31 };
|
||||
const unsigned char *p = value;
|
||||
unsigned year, mon, day, hour, min, sec, mon_len;
|
||||
|
||||
#define dec2bin(X) ({ unsigned char x = (X) - '0'; if (x > 9) goto invalid_time; x; })
|
||||
#define DD2bin(P) ({ unsigned x = dec2bin(P[0]) * 10 + dec2bin(P[1]); P += 2; x; })
|
||||
|
||||
if (tag == ASN1_UNITIM) {
|
||||
/* UTCTime: YYMMDDHHMMSSZ */
|
||||
if (vlen != 13)
|
||||
goto unsupported_time;
|
||||
year = DD2bin(p);
|
||||
if (year >= 50)
|
||||
year += 1900;
|
||||
else
|
||||
year += 2000;
|
||||
} else if (tag == ASN1_GENTIM) {
|
||||
/* GenTime: YYYYMMDDHHMMSSZ */
|
||||
if (vlen != 15)
|
||||
goto unsupported_time;
|
||||
year = DD2bin(p) * 100 + DD2bin(p);
|
||||
if (year >= 1950 && year <= 2049)
|
||||
goto invalid_time;
|
||||
} else {
|
||||
goto unsupported_time;
|
||||
}
|
||||
|
||||
mon = DD2bin(p);
|
||||
day = DD2bin(p);
|
||||
hour = DD2bin(p);
|
||||
min = DD2bin(p);
|
||||
sec = DD2bin(p);
|
||||
|
||||
if (*p != 'Z')
|
||||
goto unsupported_time;
|
||||
|
||||
if (year < 1970 ||
|
||||
mon < 1 || mon > 12)
|
||||
goto invalid_time;
|
||||
|
||||
mon_len = month_lengths[mon - 1];
|
||||
if (mon == 2) {
|
||||
if (year % 4 == 0) {
|
||||
mon_len = 29;
|
||||
if (year % 100 == 0) {
|
||||
mon_len = 28;
|
||||
if (year % 400 == 0)
|
||||
mon_len = 29;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (day < 1 || day > mon_len ||
|
||||
hour > 24 || /* ISO 8601 permits 24:00:00 as midnight tomorrow */
|
||||
min > 59 ||
|
||||
sec > 60) /* ISO 8601 permits leap seconds [X.680 46.3] */
|
||||
goto invalid_time;
|
||||
|
||||
*_t = mktime64(year, mon, day, hour, min, sec);
|
||||
return 0;
|
||||
|
||||
unsupported_time:
|
||||
pr_debug("Got unsupported time [tag %02x]: '%*phN'\n",
|
||||
tag, (int)vlen, value);
|
||||
return -EBADMSG;
|
||||
invalid_time:
|
||||
pr_debug("Got invalid time [tag %02x]: '%*phN'\n",
|
||||
tag, (int)vlen, value);
|
||||
return -EBADMSG;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(x509_decode_time);
|
||||
|
||||
int x509_note_not_before(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
return x509_decode_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen);
|
||||
}
|
||||
|
||||
int x509_note_not_after(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
return x509_decode_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note a key identifier-based AuthorityKeyIdentifier
|
||||
*/
|
||||
int x509_akid_note_kid(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
struct asymmetric_key_id *kid;
|
||||
|
||||
pr_debug("AKID: keyid: %*phN\n", (int)vlen, value);
|
||||
|
||||
if (ctx->cert->sig->auth_ids[1])
|
||||
return 0;
|
||||
|
||||
kid = asymmetric_key_generate_id(value, vlen, "", 0);
|
||||
if (IS_ERR(kid))
|
||||
return PTR_ERR(kid);
|
||||
pr_debug("authkeyid %*phN\n", kid->len, kid->data);
|
||||
ctx->cert->sig->auth_ids[1] = kid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note a directoryName in an AuthorityKeyIdentifier
|
||||
*/
|
||||
int x509_akid_note_name(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
|
||||
pr_debug("AKID: name: %*phN\n", (int)vlen, value);
|
||||
|
||||
ctx->akid_raw_issuer = value;
|
||||
ctx->akid_raw_issuer_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note a serial number in an AuthorityKeyIdentifier
|
||||
*/
|
||||
int x509_akid_note_serial(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
struct asymmetric_key_id *kid;
|
||||
|
||||
pr_debug("AKID: serial: %*phN\n", (int)vlen, value);
|
||||
|
||||
if (!ctx->akid_raw_issuer || ctx->cert->sig->auth_ids[0])
|
||||
return 0;
|
||||
|
||||
kid = asymmetric_key_generate_id(value,
|
||||
vlen,
|
||||
ctx->akid_raw_issuer,
|
||||
ctx->akid_raw_issuer_size);
|
||||
if (IS_ERR(kid))
|
||||
return PTR_ERR(kid);
|
||||
|
||||
pr_debug("authkeyid %*phN\n", kid->len, kid->data);
|
||||
ctx->cert->sig->auth_ids[0] = kid;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/* X.509 certificate parser internal definitions
|
||||
*
|
||||
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*/
|
||||
|
||||
#include <linux/time.h>
|
||||
#include <crypto/public_key.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
|
||||
struct x509_certificate {
|
||||
struct x509_certificate *next;
|
||||
struct x509_certificate *signer; /* Certificate that signed this one */
|
||||
struct public_key *pub; /* Public key details */
|
||||
struct public_key_signature *sig; /* Signature parameters */
|
||||
char *issuer; /* Name of certificate issuer */
|
||||
char *subject; /* Name of certificate subject */
|
||||
struct asymmetric_key_id *id; /* Issuer + Serial number */
|
||||
struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */
|
||||
time64_t valid_from;
|
||||
time64_t valid_to;
|
||||
const void *tbs; /* Signed data */
|
||||
unsigned tbs_size; /* Size of signed data */
|
||||
unsigned raw_sig_size; /* Size of sigature */
|
||||
const void *raw_sig; /* Signature data */
|
||||
const void *raw_serial; /* Raw serial number in ASN.1 */
|
||||
unsigned raw_serial_size;
|
||||
unsigned raw_issuer_size;
|
||||
const void *raw_issuer; /* Raw issuer name in ASN.1 */
|
||||
const void *raw_subject; /* Raw subject name in ASN.1 */
|
||||
unsigned raw_subject_size;
|
||||
unsigned raw_skid_size;
|
||||
const void *raw_skid; /* Raw subjectKeyId in ASN.1 */
|
||||
unsigned index;
|
||||
bool seen; /* Infinite recursion prevention */
|
||||
bool verified;
|
||||
bool self_signed; /* T if self-signed (check unsupported_sig too) */
|
||||
bool unsupported_key; /* T if key uses unsupported crypto */
|
||||
bool unsupported_sig; /* T if signature uses unsupported crypto */
|
||||
bool blacklisted;
|
||||
};
|
||||
|
||||
/*
|
||||
* x509_cert_parser.c
|
||||
*/
|
||||
extern void x509_free_certificate(struct x509_certificate *cert);
|
||||
extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
|
||||
extern int x509_decode_time(time64_t *_t, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const unsigned char *value, size_t vlen);
|
||||
|
||||
/*
|
||||
* x509_public_key.c
|
||||
*/
|
||||
extern int x509_get_sig_params(struct x509_certificate *cert);
|
||||
extern int x509_check_for_self_signed(struct x509_certificate *cert);
|
||||
@@ -0,0 +1,292 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/* Instantiate a public key crypto key from an X.509 Certificate
|
||||
*
|
||||
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "X.509: "fmt
|
||||
#ifdef __UBOOT__
|
||||
#include <common.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/errno.h>
|
||||
#else
|
||||
#include <linux/module.h>
|
||||
#endif
|
||||
#include <linux/kernel.h>
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/slab.h>
|
||||
#include <keys/asymmetric-subtype.h>
|
||||
#include <keys/asymmetric-parser.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include <crypto/hash.h>
|
||||
#include "asymmetric_keys.h"
|
||||
#endif
|
||||
#include "x509_parser.h"
|
||||
|
||||
/*
|
||||
* Set up the signature parameters in an X.509 certificate. This involves
|
||||
* digesting the signed data and extracting the signature.
|
||||
*/
|
||||
int x509_get_sig_params(struct x509_certificate *cert)
|
||||
{
|
||||
struct public_key_signature *sig = cert->sig;
|
||||
#ifndef __UBOOT__
|
||||
struct crypto_shash *tfm;
|
||||
struct shash_desc *desc;
|
||||
size_t desc_size;
|
||||
#endif
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
if (!cert->pub->pkey_algo)
|
||||
cert->unsupported_key = true;
|
||||
|
||||
if (!sig->pkey_algo)
|
||||
cert->unsupported_sig = true;
|
||||
|
||||
/* We check the hash if we can - even if we can't then verify it */
|
||||
if (!sig->hash_algo) {
|
||||
cert->unsupported_sig = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sig->s = kmemdup(cert->raw_sig, cert->raw_sig_size, GFP_KERNEL);
|
||||
if (!sig->s)
|
||||
return -ENOMEM;
|
||||
|
||||
sig->s_size = cert->raw_sig_size;
|
||||
|
||||
#ifdef __UBOOT__
|
||||
/*
|
||||
* Note:
|
||||
* This part (filling sig->digest) should be implemented if
|
||||
* x509_check_for_self_signed() is enabled x509_cert_parse().
|
||||
* Currently, this check won't affect UEFI secure boot.
|
||||
*/
|
||||
ret = 0;
|
||||
#else
|
||||
/* Allocate the hashing algorithm we're going to need and find out how
|
||||
* big the hash operational data will be.
|
||||
*/
|
||||
tfm = crypto_alloc_shash(sig->hash_algo, 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
if (PTR_ERR(tfm) == -ENOENT) {
|
||||
cert->unsupported_sig = true;
|
||||
return 0;
|
||||
}
|
||||
return PTR_ERR(tfm);
|
||||
}
|
||||
|
||||
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
|
||||
sig->digest_size = crypto_shash_digestsize(tfm);
|
||||
|
||||
ret = -ENOMEM;
|
||||
sig->digest = kmalloc(sig->digest_size, GFP_KERNEL);
|
||||
if (!sig->digest)
|
||||
goto error;
|
||||
|
||||
desc = kzalloc(desc_size, GFP_KERNEL);
|
||||
if (!desc)
|
||||
goto error;
|
||||
|
||||
desc->tfm = tfm;
|
||||
|
||||
ret = crypto_shash_digest(desc, cert->tbs, cert->tbs_size, sig->digest);
|
||||
if (ret < 0)
|
||||
goto error_2;
|
||||
|
||||
ret = is_hash_blacklisted(sig->digest, sig->digest_size, "tbs");
|
||||
if (ret == -EKEYREJECTED) {
|
||||
pr_err("Cert %*phN is blacklisted\n",
|
||||
sig->digest_size, sig->digest);
|
||||
cert->blacklisted = true;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
error_2:
|
||||
kfree(desc);
|
||||
error:
|
||||
crypto_free_shash(tfm);
|
||||
#endif /* __UBOOT__ */
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/*
|
||||
* Check for self-signedness in an X.509 cert and if found, check the signature
|
||||
* immediately if we can.
|
||||
*/
|
||||
int x509_check_for_self_signed(struct x509_certificate *cert)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
if (cert->raw_subject_size != cert->raw_issuer_size ||
|
||||
memcmp(cert->raw_subject, cert->raw_issuer,
|
||||
cert->raw_issuer_size) != 0)
|
||||
goto not_self_signed;
|
||||
|
||||
if (cert->sig->auth_ids[0] || cert->sig->auth_ids[1]) {
|
||||
/* If the AKID is present it may have one or two parts. If
|
||||
* both are supplied, both must match.
|
||||
*/
|
||||
bool a = asymmetric_key_id_same(cert->skid, cert->sig->auth_ids[1]);
|
||||
bool b = asymmetric_key_id_same(cert->id, cert->sig->auth_ids[0]);
|
||||
|
||||
if (!a && !b)
|
||||
goto not_self_signed;
|
||||
|
||||
ret = -EKEYREJECTED;
|
||||
if (((a && !b) || (b && !a)) &&
|
||||
cert->sig->auth_ids[0] && cert->sig->auth_ids[1])
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = -EKEYREJECTED;
|
||||
if (strcmp(cert->pub->pkey_algo, cert->sig->pkey_algo) != 0)
|
||||
goto out;
|
||||
|
||||
ret = public_key_verify_signature(cert->pub, cert->sig);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOPKG) {
|
||||
cert->unsupported_sig = true;
|
||||
ret = 0;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
pr_devel("Cert Self-signature verified");
|
||||
cert->self_signed = true;
|
||||
|
||||
out:
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
|
||||
not_self_signed:
|
||||
pr_devel("<==%s() = 0 [not]\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to parse a data blob for a key as an X509 certificate.
|
||||
*/
|
||||
static int x509_key_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct asymmetric_key_ids *kids;
|
||||
struct x509_certificate *cert;
|
||||
const char *q;
|
||||
size_t srlen, sulen;
|
||||
char *desc = NULL, *p;
|
||||
int ret;
|
||||
|
||||
cert = x509_cert_parse(prep->data, prep->datalen);
|
||||
if (IS_ERR(cert))
|
||||
return PTR_ERR(cert);
|
||||
|
||||
pr_devel("Cert Issuer: %s\n", cert->issuer);
|
||||
pr_devel("Cert Subject: %s\n", cert->subject);
|
||||
|
||||
if (cert->unsupported_key) {
|
||||
ret = -ENOPKG;
|
||||
goto error_free_cert;
|
||||
}
|
||||
|
||||
pr_devel("Cert Key Algo: %s\n", cert->pub->pkey_algo);
|
||||
pr_devel("Cert Valid period: %lld-%lld\n", cert->valid_from, cert->valid_to);
|
||||
|
||||
cert->pub->id_type = "X509";
|
||||
|
||||
if (cert->unsupported_sig) {
|
||||
public_key_signature_free(cert->sig);
|
||||
cert->sig = NULL;
|
||||
} else {
|
||||
pr_devel("Cert Signature: %s + %s\n",
|
||||
cert->sig->pkey_algo, cert->sig->hash_algo);
|
||||
}
|
||||
|
||||
/* Don't permit addition of blacklisted keys */
|
||||
ret = -EKEYREJECTED;
|
||||
if (cert->blacklisted)
|
||||
goto error_free_cert;
|
||||
|
||||
/* Propose a description */
|
||||
sulen = strlen(cert->subject);
|
||||
if (cert->raw_skid) {
|
||||
srlen = cert->raw_skid_size;
|
||||
q = cert->raw_skid;
|
||||
} else {
|
||||
srlen = cert->raw_serial_size;
|
||||
q = cert->raw_serial;
|
||||
}
|
||||
|
||||
ret = -ENOMEM;
|
||||
desc = kmalloc(sulen + 2 + srlen * 2 + 1, GFP_KERNEL);
|
||||
if (!desc)
|
||||
goto error_free_cert;
|
||||
p = memcpy(desc, cert->subject, sulen);
|
||||
p += sulen;
|
||||
*p++ = ':';
|
||||
*p++ = ' ';
|
||||
p = bin2hex(p, q, srlen);
|
||||
*p = 0;
|
||||
|
||||
kids = kmalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL);
|
||||
if (!kids)
|
||||
goto error_free_desc;
|
||||
kids->id[0] = cert->id;
|
||||
kids->id[1] = cert->skid;
|
||||
|
||||
/* We're pinning the module by being linked against it */
|
||||
__module_get(public_key_subtype.owner);
|
||||
prep->payload.data[asym_subtype] = &public_key_subtype;
|
||||
prep->payload.data[asym_key_ids] = kids;
|
||||
prep->payload.data[asym_crypto] = cert->pub;
|
||||
prep->payload.data[asym_auth] = cert->sig;
|
||||
prep->description = desc;
|
||||
prep->quotalen = 100;
|
||||
|
||||
/* We've finished with the certificate */
|
||||
cert->pub = NULL;
|
||||
cert->id = NULL;
|
||||
cert->skid = NULL;
|
||||
cert->sig = NULL;
|
||||
desc = NULL;
|
||||
ret = 0;
|
||||
|
||||
error_free_desc:
|
||||
kfree(desc);
|
||||
error_free_cert:
|
||||
x509_free_certificate(cert);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct asymmetric_key_parser x509_key_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "x509",
|
||||
.parse = x509_key_preparse,
|
||||
};
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
static int __init x509_key_init(void)
|
||||
{
|
||||
return register_asymmetric_key_parser(&x509_key_parser);
|
||||
}
|
||||
|
||||
static void __exit x509_key_exit(void)
|
||||
{
|
||||
unregister_asymmetric_key_parser(&x509_key_parser);
|
||||
}
|
||||
|
||||
module_init(x509_key_init);
|
||||
module_exit(x509_key_exit);
|
||||
#endif /* !__UBOOT__ */
|
||||
|
||||
MODULE_DESCRIPTION("X.509 certificate parser");
|
||||
MODULE_AUTHOR("Red Hat, Inc.");
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user