config PUBLIC_KEY_ALGO_RSA
tristate "RSA public-key algorithm"
- select CRYPTO_RSA
+ select MPILIB
help
This option enables support for the RSA algorithm (PKCS#1, RFC3447).
x509_key_parser-y := \
x509-asn1.o \
x509_akid-asn1.o \
+ x509_rsakey-asn1.o \
x509_cert_parser.o \
x509_public_key.o
$(obj)/x509_cert_parser.o: \
$(obj)/x509-asn1.h \
- $(obj)/x509_akid-asn1.h
-
+ $(obj)/x509_akid-asn1.h \
+ $(obj)/x509_rsakey-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
+$(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h
clean-files += x509-asn1.c x509-asn1.h
clean-files += x509_akid-asn1.c x509_akid-asn1.h
+clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h
#
# PKCS#7 message handling
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/oid_registry.h>
-#include <crypto/public_key.h>
+#include "public_key.h"
#include "pkcs7_parser.h"
#include "pkcs7-asn1.h"
static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)
{
if (sinfo) {
- kfree(sinfo->sig.s);
+ mpi_free(sinfo->sig.mpi[0]);
kfree(sinfo->sig.digest);
kfree(sinfo->signing_cert_id);
kfree(sinfo);
const void *value, size_t vlen)
{
struct pkcs7_parse_context *ctx = context;
+ MPI mpi;
BUG_ON(ctx->sinfo->sig.pkey_algo != PKEY_ALGO_RSA);
- ctx->sinfo->sig.s = kmemdup(value, vlen, GFP_KERNEL);
- if (!ctx->sinfo->sig.s)
+ mpi = mpi_read_raw_data(value, vlen);
+ if (!mpi)
return -ENOMEM;
- ctx->sinfo->sig.s_size = vlen;
+ ctx->sinfo->sig.mpi[0] = mpi;
+ ctx->sinfo->sig.nr_mpi = 1;
return 0;
}
#include <linux/asn1.h>
#include <linux/key.h>
#include <keys/asymmetric-type.h>
-#include <crypto/public_key.h>
+#include "public_key.h"
#include "pkcs7_parser.h"
/**
#include <linux/err.h>
#include <linux/asn1.h>
#include <crypto/hash.h>
-#include <crypto/public_key.h>
+#include "public_key.h"
#include "pkcs7_parser.h"
/*
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <keys/asymmetric-subtype.h>
-#include <crypto/public_key.h>
+#include "public_key.h"
MODULE_LICENSE("GPL");
const char *const pkey_algo_name[PKEY_ALGO__LAST] = {
- [PKEY_ALGO_DSA] = "dsa",
- [PKEY_ALGO_RSA] = "rsa",
+ [PKEY_ALGO_DSA] = "DSA",
+ [PKEY_ALGO_RSA] = "RSA",
};
EXPORT_SYMBOL_GPL(pkey_algo_name);
+const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST] = {
+#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \
+ defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE)
+ [PKEY_ALGO_RSA] = &RSA_public_key_algorithm,
+#endif
+};
+EXPORT_SYMBOL_GPL(pkey_algo);
+
const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST] = {
[PKEY_ID_PGP] = "PGP",
[PKEY_ID_X509] = "X509",
};
EXPORT_SYMBOL_GPL(pkey_id_type_name);
-static int (*alg_verify[PKEY_ALGO__LAST])(const struct public_key *pkey,
- const struct public_key_signature *sig) = {
- NULL,
- rsa_verify_signature
-};
-
/*
* Provide a part of a description of the key for /proc/keys.
*/
if (key)
seq_printf(m, "%s.%s",
- pkey_id_type_name[key->id_type],
- pkey_algo_name[key->pkey_algo]);
+ pkey_id_type_name[key->id_type], key->algo->name);
}
/*
void public_key_destroy(void *payload)
{
struct public_key *key = payload;
+ int i;
- if (key)
- kfree(key->key);
- kfree(key);
+ if (key) {
+ for (i = 0; i < ARRAY_SIZE(key->mpi); i++)
+ mpi_free(key->mpi[i]);
+ kfree(key);
+ }
}
EXPORT_SYMBOL_GPL(public_key_destroy);
/*
* Verify a signature using a public key.
*/
-int public_key_verify_signature(const struct public_key *pkey,
+int public_key_verify_signature(const struct public_key *pk,
const struct public_key_signature *sig)
{
- BUG_ON(!pkey);
+ const struct public_key_algorithm *algo;
+
+ BUG_ON(!pk);
+ BUG_ON(!pk->mpi[0]);
+ BUG_ON(!pk->mpi[1]);
BUG_ON(!sig);
BUG_ON(!sig->digest);
- BUG_ON(!sig->s);
+ BUG_ON(!sig->mpi[0]);
+
+ algo = pk->algo;
+ if (!algo) {
+ if (pk->pkey_algo >= PKEY_ALGO__LAST)
+ return -ENOPKG;
+ algo = pkey_algo[pk->pkey_algo];
+ if (!algo)
+ return -ENOPKG;
+ }
- if (pkey->pkey_algo >= PKEY_ALGO__LAST)
- return -ENOPKG;
+ if (!algo->verify_signature)
+ return -ENOTSUPP;
- if (!alg_verify[pkey->pkey_algo])
- return -ENOPKG;
+ if (sig->nr_mpi != algo->n_sig_mpi) {
+ pr_debug("Signature has %u MPI not %u\n",
+ sig->nr_mpi, algo->n_sig_mpi);
+ return -EINVAL;
+ }
- return alg_verify[pkey->pkey_algo](pkey, sig);
+ return algo->verify_signature(pk, sig);
}
EXPORT_SYMBOL_GPL(public_key_verify_signature);
--- /dev/null
+/* Public key algorithm internals
+ *
+ * See Documentation/crypto/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <crypto/public_key.h>
+
+extern struct asymmetric_key_subtype public_key_subtype;
+
+/*
+ * Public key algorithm definition.
+ */
+struct public_key_algorithm {
+ const char *name;
+ u8 n_pub_mpi; /* Number of MPIs in public key */
+ u8 n_sec_mpi; /* Number of MPIs in secret key */
+ u8 n_sig_mpi; /* Number of MPIs in a signature */
+ int (*verify_signature)(const struct public_key *key,
+ const struct public_key_signature *sig);
+};
+
+extern const struct public_key_algorithm RSA_public_key_algorithm;
+
+/*
+ * public_key.c
+ */
+extern int public_key_verify_signature(const struct public_key *pk,
+ const struct public_key_signature *sig);
#define pr_fmt(fmt) "RSA: "fmt
#include <linux/module.h>
+#include <linux/kernel.h>
#include <linux/slab.h>
-#include <crypto/akcipher.h>
-#include <crypto/public_key.h>
#include <crypto/algapi.h>
+#include "public_key.h"
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RSA Public Key Algorithm");
#undef _
};
-struct rsa_completion {
- struct completion completion;
- int err;
-};
+/*
+ * RSAVP1() function [RFC3447 sec 5.2.2]
+ */
+static int RSAVP1(const struct public_key *key, MPI s, MPI *_m)
+{
+ MPI m;
+ int ret;
+
+ /* (1) Validate 0 <= s < n */
+ if (mpi_cmp_ui(s, 0) < 0) {
+ kleave(" = -EBADMSG [s < 0]");
+ return -EBADMSG;
+ }
+ if (mpi_cmp(s, key->rsa.n) >= 0) {
+ kleave(" = -EBADMSG [s >= n]");
+ return -EBADMSG;
+ }
+
+ m = mpi_alloc(0);
+ if (!m)
+ return -ENOMEM;
+
+ /* (2) m = s^e mod n */
+ ret = mpi_powm(m, s, key->rsa.e, key->rsa.n);
+ if (ret < 0) {
+ mpi_free(m);
+ return ret;
+ }
+
+ *_m = m;
+ return 0;
+}
+
+/*
+ * Integer to Octet String conversion [RFC3447 sec 4.1]
+ */
+static int RSA_I2OSP(MPI x, size_t xLen, u8 **pX)
+{
+ unsigned X_size, x_size;
+ int X_sign;
+ u8 *X;
+
+ /* Make sure the string is the right length. The number should begin
+ * with { 0x00, 0x01, ... } so we have to account for 15 leading zero
+ * bits not being reported by MPI.
+ */
+ x_size = mpi_get_nbits(x);
+ pr_devel("size(x)=%u xLen*8=%zu\n", x_size, xLen * 8);
+ if (x_size != xLen * 8 - 15)
+ return -ERANGE;
+
+ X = mpi_get_buffer(x, &X_size, &X_sign);
+ if (!X)
+ return -ENOMEM;
+ if (X_sign < 0) {
+ kfree(X);
+ return -EBADMSG;
+ }
+ if (X_size != xLen - 1) {
+ kfree(X);
+ return -EBADMSG;
+ }
+
+ *pX = X;
+ return 0;
+}
/*
* Perform the RSA signature verification.
* @asn1_template: The DigestInfo ASN.1 template
* @asn1_size: Size of asm1_template[]
*/
-static int rsa_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size,
+static int RSA_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size,
const u8 *asn1_template, size_t asn1_size)
{
unsigned PS_end, T_offset, i;
if (k < 2 + 1 + asn1_size + hash_size)
return -EBADMSG;
- /* Decode the EMSA-PKCS1-v1_5
- * note: leading zeros are stirpped by the RSA implementation */
- if (EM[0] != 0x01) {
- kleave(" = -EBADMSG [EM[0] == %02u]", EM[0]);
+
+ /* Decode the EMSA-PKCS1-v1_5 */
+ if (EM[1] != 0x01) {
+ kleave(" = -EBADMSG [EM[1] == %02u]", EM[1]);
return -EBADMSG;
}
return -EBADMSG;
}
- for (i = 1; i < PS_end; i++) {
+ for (i = 2; i < PS_end; i++) {
if (EM[i] != 0xff) {
kleave(" = -EBADMSG [EM[PS%x] == %02u]", i - 2, EM[i]);
return -EBADMSG;
return 0;
}
-static void public_key_verify_done(struct crypto_async_request *req, int err)
+/*
+ * Perform the verification step [RFC3447 sec 8.2.2].
+ */
+static int RSA_verify_signature(const struct public_key *key,
+ const struct public_key_signature *sig)
{
- struct rsa_completion *compl = req->data;
+ size_t tsize;
+ int ret;
- if (err == -EINPROGRESS)
- return;
+ /* Variables as per RFC3447 sec 8.2.2 */
+ const u8 *H = sig->digest;
+ u8 *EM = NULL;
+ MPI m = NULL;
+ size_t k;
- compl->err = err;
- complete(&compl->completion);
-}
+ kenter("");
-int rsa_verify_signature(const struct public_key *pkey,
- const struct public_key_signature *sig)
-{
- struct crypto_akcipher *tfm;
- struct akcipher_request *req;
- struct rsa_completion compl;
- struct scatterlist sig_sg, sg_out;
- void *outbuf = NULL;
- unsigned int outlen = 0;
- int ret = -ENOMEM;
-
- tfm = crypto_alloc_akcipher("rsa", 0, 0);
- if (IS_ERR(tfm))
- goto error_out;
-
- req = akcipher_request_alloc(tfm, GFP_KERNEL);
- if (!req)
- goto error_free_tfm;
-
- ret = crypto_akcipher_set_pub_key(tfm, pkey->key, pkey->keylen);
- if (ret)
- goto error_free_req;
-
- ret = -EINVAL;
- outlen = crypto_akcipher_maxsize(tfm);
- if (!outlen)
- goto error_free_req;
-
- /* initlialzie out buf */
- ret = -ENOMEM;
- outbuf = kmalloc(outlen, GFP_KERNEL);
- if (!outbuf)
- goto error_free_req;
-
- sg_init_one(&sig_sg, sig->s, sig->s_size);
- sg_init_one(&sg_out, outbuf, outlen);
- akcipher_request_set_crypt(req, &sig_sg, &sg_out, sig->s_size, outlen);
- init_completion(&compl.completion);
- akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
- CRYPTO_TFM_REQ_MAY_SLEEP,
- public_key_verify_done, &compl);
-
- ret = crypto_akcipher_verify(req);
- if (ret == -EINPROGRESS) {
- wait_for_completion(&compl.completion);
- ret = compl.err;
+ if (!RSA_ASN1_templates[sig->pkey_hash_algo].data)
+ return -ENOTSUPP;
+
+ /* (1) Check the signature size against the public key modulus size */
+ k = mpi_get_nbits(key->rsa.n);
+ tsize = mpi_get_nbits(sig->rsa.s);
+
+ /* According to RFC 4880 sec 3.2, length of MPI is computed starting
+ * from most significant bit. So the RFC 3447 sec 8.2.2 size check
+ * must be relaxed to conform with shorter signatures - so we fail here
+ * only if signature length is longer than modulus size.
+ */
+ pr_devel("step 1: k=%zu size(S)=%zu\n", k, tsize);
+ if (k < tsize) {
+ ret = -EBADMSG;
+ goto error;
}
- if (ret)
- goto error_free_req;
+ /* Round up and convert to octets */
+ k = (k + 7) / 8;
- /*
- * Output from the operation is an encoded message (EM) of
- * length k octets.
+ /* (2b) Apply the RSAVP1 verification primitive to the public key */
+ ret = RSAVP1(key, sig->rsa.s, &m);
+ if (ret < 0)
+ goto error;
+
+ /* (2c) Convert the message representative (m) to an encoded message
+ * (EM) of length k octets.
+ *
+ * NOTE! The leading zero byte is suppressed by MPI, so we pass a
+ * pointer to the _preceding_ byte to RSA_verify()!
*/
- outlen = req->dst_len;
- ret = rsa_verify(sig->digest, outbuf, outlen, sig->digest_size,
+ ret = RSA_I2OSP(m, k, &EM);
+ if (ret < 0)
+ goto error;
+
+ ret = RSA_verify(H, EM - 1, k, sig->digest_size,
RSA_ASN1_templates[sig->pkey_hash_algo].data,
RSA_ASN1_templates[sig->pkey_hash_algo].size);
-error_free_req:
- akcipher_request_free(req);
-error_free_tfm:
- crypto_free_akcipher(tfm);
-error_out:
- kfree(outbuf);
+
+error:
+ kfree(EM);
+ mpi_free(m);
+ kleave(" = %d", ret);
return ret;
}
-EXPORT_SYMBOL_GPL(rsa_verify_signature);
+
+const struct public_key_algorithm RSA_public_key_algorithm = {
+ .name = "RSA",
+ .n_pub_mpi = 2,
+ .n_sec_mpi = 3,
+ .n_sig_mpi = 1,
+ .verify_signature = RSA_verify_signature,
+};
+EXPORT_SYMBOL_GPL(RSA_public_key_algorithm);
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/oid_registry.h>
-#include <crypto/public_key.h>
+#include "public_key.h"
#include "x509_parser.h"
#include "x509-asn1.h"
#include "x509_akid-asn1.h"
+#include "x509_rsakey-asn1.h"
struct x509_parse_context {
struct x509_certificate *cert; /* Certificate being constructed */
kfree(cert->akid_id);
kfree(cert->akid_skid);
kfree(cert->sig.digest);
- kfree(cert->sig.s);
+ mpi_free(cert->sig.rsa.s);
kfree(cert);
}
}
}
}
- cert->pub->key = kmemdup(ctx->key, ctx->key_size, GFP_KERNEL);
- if (!cert->pub->key)
+ /* Decode the public key */
+ ret = asn1_ber_decoder(&x509_rsakey_decoder, ctx,
+ ctx->key, ctx->key_size);
+ if (ret < 0)
goto error_decode;
- cert->pub->keylen = ctx->key_size;
-
/* Generate cert issuer + serial number key ID */
kid = asymmetric_key_generate_id(cert->raw_serial,
cert->raw_serial_size,
return cert;
error_decode:
- kfree(cert->pub->key);
kfree(ctx);
error_no_ctx:
x509_free_certificate(cert);
return 0;
}
+/*
+ * Extract a RSA public key value
+ */
+int rsa_extract_mpi(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct x509_parse_context *ctx = context;
+ MPI mpi;
+
+ if (ctx->nr_mpi >= ARRAY_SIZE(ctx->cert->pub->mpi)) {
+ pr_err("Too many public key MPIs in certificate\n");
+ return -EBADMSG;
+ }
+
+ mpi = mpi_read_raw_data(value, vlen);
+ if (!mpi)
+ return -ENOMEM;
+
+ ctx->cert->pub->mpi[ctx->nr_mpi++] = mpi;
+ return 0;
+}
+
/* The keyIdentifier in AuthorityKeyIdentifier SEQUENCE is tag(CONT,PRIM,0) */
#define SEQ_TAG_KEYID (ASN1_CONT << 6)
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/mpi.h>
+#include <linux/asn1_decoder.h>
#include <keys/asymmetric-subtype.h>
#include <keys/asymmetric-parser.h>
#include <keys/system_keyring.h>
#include <crypto/hash.h>
#include "asymmetric_keys.h"
+#include "public_key.h"
#include "x509_parser.h"
static bool use_builtin_keys;
if (cert->unsupported_crypto)
return -ENOPKG;
- if (cert->sig.s)
+ if (cert->sig.rsa.s)
return 0;
- cert->sig.s = kmemdup(cert->raw_sig, cert->raw_sig_size,
- GFP_KERNEL);
- if (!cert->sig.s)
+ cert->sig.rsa.s = mpi_read_raw_data(cert->raw_sig, cert->raw_sig_size);
+ if (!cert->sig.rsa.s)
return -ENOMEM;
-
- cert->sig.s_size = cert->raw_sig_size;
+ cert->sig.nr_mpi = 1;
/* Allocate the hashing algorithm we're going to need and find out how
* big the hash operational data will be.
if (cert->pub->pkey_algo >= PKEY_ALGO__LAST ||
cert->sig.pkey_algo >= PKEY_ALGO__LAST ||
cert->sig.pkey_hash_algo >= PKEY_HASH__LAST ||
+ !pkey_algo[cert->pub->pkey_algo] ||
+ !pkey_algo[cert->sig.pkey_algo] ||
!hash_algo_name[cert->sig.pkey_hash_algo]) {
ret = -ENOPKG;
goto error_free_cert;
pkey_algo_name[cert->sig.pkey_algo],
hash_algo_name[cert->sig.pkey_hash_algo]);
+ cert->pub->algo = pkey_algo[cert->pub->pkey_algo];
cert->pub->id_type = PKEY_ID_X509;
/* Check the signature on the key if it appears to be self-signed */
--- /dev/null
+RSAPublicKey ::= SEQUENCE {
+ modulus INTEGER ({ rsa_extract_mpi }), -- n
+ publicExponent INTEGER ({ rsa_extract_mpi }) -- e
+ }
#ifndef _LINUX_PUBLIC_KEY_H
#define _LINUX_PUBLIC_KEY_H
+#include <linux/mpi.h>
#include <crypto/hash_info.h>
enum pkey_algo {
};
extern const char *const pkey_algo_name[PKEY_ALGO__LAST];
+extern const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST];
/* asymmetric key implementation supports only up to SHA224 */
#define PKEY_HASH__LAST (HASH_ALGO_SHA224 + 1)
* part.
*/
struct public_key {
- void *key;
- u32 keylen;
+ const struct public_key_algorithm *algo;
+ u8 capabilities;
+#define PKEY_CAN_ENCRYPT 0x01
+#define PKEY_CAN_DECRYPT 0x02
+#define PKEY_CAN_SIGN 0x04
+#define PKEY_CAN_VERIFY 0x08
enum pkey_algo pkey_algo : 8;
enum pkey_id_type id_type : 8;
+ union {
+ MPI mpi[5];
+ struct {
+ MPI p; /* DSA prime */
+ MPI q; /* DSA group order */
+ MPI g; /* DSA group generator */
+ MPI y; /* DSA public-key value = g^x mod p */
+ MPI x; /* DSA secret exponent (if present) */
+ } dsa;
+ struct {
+ MPI n; /* RSA public modulus */
+ MPI e; /* RSA public encryption exponent */
+ MPI d; /* RSA secret encryption exponent (if present) */
+ MPI p; /* RSA secret prime (if present) */
+ MPI q; /* RSA secret prime (if present) */
+ } rsa;
+ };
};
extern void public_key_destroy(void *payload);
* Public key cryptography signature data
*/
struct public_key_signature {
- u8 *s; /* Signature */
- u32 s_size; /* Number of bytes in signature */
u8 *digest;
- u8 digest_size; /* Number of bytes in digest */
+ u8 digest_size; /* Number of bytes in digest */
+ u8 nr_mpi; /* Occupancy of mpi[] */
enum pkey_algo pkey_algo : 8;
enum hash_algo pkey_hash_algo : 8;
+ union {
+ MPI mpi[2];
+ struct {
+ MPI s; /* m^d mod n */
+ } rsa;
+ struct {
+ MPI r;
+ MPI s;
+ } dsa;
+ };
};
-extern struct asymmetric_key_subtype public_key_subtype;
struct key;
extern int verify_signature(const struct key *key,
const struct public_key_signature *sig);
const struct asymmetric_key_id *skid,
bool partial);
-int public_key_verify_signature(const struct public_key *pkey,
- const struct public_key_signature *sig);
-
-int rsa_verify_signature(const struct public_key *pkey,
- const struct public_key_signature *sig);
#endif /* _LINUX_PUBLIC_KEY_H */
select ASYMMETRIC_KEY_TYPE
select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
select PUBLIC_KEY_ALGO_RSA
- select CRYPTO_RSA
select X509_CERTIFICATE_PARSER
help
This option enables digital signature verification using
pks.pkey_hash_algo = hdr->hash_algo;
pks.digest = (u8 *)data;
pks.digest_size = datalen;
- pks.s = hdr->sig;
- pks.s_size = siglen;
- ret = verify_signature(key, &pks);
+ pks.nr_mpi = 1;
+ pks.rsa.s = mpi_read_raw_data(hdr->sig, siglen);
+
+ if (pks.rsa.s)
+ ret = verify_signature(key, &pks);
+
+ mpi_free(pks.rsa.s);
key_put(key);
pr_debug("%s() = %d\n", __func__, ret);
return ret;