diff options
Diffstat (limited to 'compat/verification/public_key.c')
-rw-r--r-- | compat/verification/public_key.c | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/compat/verification/public_key.c b/compat/verification/public_key.c new file mode 100644 index 0000000..83a6935 --- /dev/null +++ b/compat/verification/public_key.c @@ -0,0 +1,131 @@ +/* + * Adapted from the kernel for simplicity in backports. + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PKEY: "fmt +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/seq_file.h> +#include <linux/scatterlist.h> +#include <linux/asn1_decoder.h> +#include <crypto/public_key.h> +#include "rsapubkey.asn1.h" +#include "mbedtls/rsa.h" +#include "mbedtls/md.h" + +void public_key_free(struct public_key *key) +{ + if (key) { + kfree(key->key); + kfree(key); + } +} + +int rsa_get_n(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + mbedtls_rsa_context *rsa = context; + + /* invalid key provided */ + if (!value || !vlen) + return -EINVAL; + + return mbedtls_mpi_read_binary(&rsa->N, value, vlen) ? -EINVAL : 0; +} + +int rsa_get_e(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + mbedtls_rsa_context *rsa = context; + + /* invalid key provided */ + if (!value || !vlen) + return -EINVAL; + + return mbedtls_mpi_read_binary(&rsa->E, value, vlen) ? -EINVAL : 0; +} + +int public_key_verify_signature(const struct public_key *pkey, + const struct public_key_signature *sig) +{ + mbedtls_rsa_context rsa; + mbedtls_md_type_t md_alg; + const u8 *sigdata = sig->s; + int s_size = sig->s_size; + int ret; + + if (WARN_ON(!pkey)) + return -EINVAL; + + if (strcmp(sig->pkey_algo, "rsa")) + return -ENOTSUPP; + + if (strcmp(sig->hash_algo, "sha1") == 0) + md_alg = MBEDTLS_MD_SHA1; + else if (strcmp(sig->hash_algo, "sha256") == 0) + md_alg = MBEDTLS_MD_SHA256; + else + return -ENOTSUPP; + + mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0); + + ret = asn1_ber_decoder(&rsapubkey_decoder, &rsa, + pkey->key, pkey->keylen); + if (ret) + goto free; + + rsa.len = (mbedtls_mpi_bitlen(&rsa.N) + 7) >> 3; + + /* + * In some cases (from X.509 certificates) we get here with a + * BIT_STRING ASN.1 object, in which the first byte indicates + * the number of unused bits in the bit string (in case the + * string isn't a multiple of 8 long). + * Assume here that it's always a multiple of 8, and just skip + * the additional byte. + */ + if (s_size == rsa.len + 1 && sigdata[0] == 0) { + sigdata = sig->s + 1; + s_size -= 1; + } + + if (rsa.len != s_size) { + ret = -EINVAL; + goto free; + } + + ret = mbedtls_rsa_pkcs1_verify(&rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, + md_alg, 0, sig->digest, sigdata); + + if (ret) + ret = -EKEYREJECTED; + else + ret = 0; + + free: + mbedtls_rsa_free(&rsa); + + return ret; +} + +void public_key_signature_free(struct public_key_signature *sig) +{ + int i; + + if (sig) { + for (i = 0; i < ARRAY_SIZE(sig->auth_ids); i++) + kfree(sig->auth_ids[i]); + kfree(sig->s); + kfree(sig->digest); + kfree(sig); + } +} |