GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -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");