Re: [RFC PATCH] X.509: Parse public key parameters from x509 for akcipher

From: Vitaly Chikunov
Date: Mon Dec 10 2018 - 14:37:59 EST


On Sun, Dec 09, 2018 at 04:55:48PM +0300, Vitaly Chikunov wrote:
> Some public key algorithms (like ECDSA) keep in parameters field
> important data such as digest and curve OIDs (possibly more for
> different ECDSA variants). Thus, just setting a public key (as
> for RSA) is not enough.
>
> Introduce set_params() callback for akcipher which will be used to
> pass BER encoded parameters array, with additional argument of
> algorithm OID.
>
> This is done with the intent of adding support for EC-RDSA (ISO/IEC
> 14888-3:2018, RFC 7091, and basically ECDSA variant) public keys (which
> will be finally used in IMA subsystem). Thus, also oid_registry.h is
> updated.
>
> Rationale:
>
> - For such keys just setting public key without parameters is
> meaningless, so it would be possible to add parameters in
> crypto_akcipher_set_pub_key (and .set_pub_key) calls. But, this will
> needlessly change API for RSA akcipher. Also, additional callback
> making it possible to pass parameters after
> crypto_akcipher_set_priv_key (and .set_priv_key) in the future.
>
> - Algorithm OID is passed to be validated in .set_params callback,
> otherwise, it could have the wrong value.
>
> - Particular algorithm OIDs are checked in x509_note_params, (because
> this is called from AlgorithmIdentifier (ASN.1) parser, which is
> called multiple times, as it's used multiple times in X.509
> certificate), to distinguish a public key call from a signature call.
>
> Signed-off-by: Vitaly Chikunov <vt@xxxxxxxxxxxx>
> ---
> crypto/asymmetric_keys/public_key.c | 16 +++++++++++++++
> crypto/asymmetric_keys/x509.asn1 | 2 +-
> crypto/asymmetric_keys/x509_cert_parser.c | 27 +++++++++++++++++++++++++
> crypto/testmgr.c | 5 +++++
> crypto/testmgr.h | 3 +++
> include/crypto/akcipher.h | 33 +++++++++++++++++++++++++++++++
> include/crypto/public_key.h | 4 ++++
> include/linux/oid_registry.h | 6 ++++++
> 8 files changed, 95 insertions(+), 1 deletion(-)
>
> diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
> index f5d85b47fcc6..3bc090b8adef 100644
> --- a/crypto/asymmetric_keys/public_key.c
> +++ b/crypto/asymmetric_keys/public_key.c
> @@ -45,6 +45,7 @@ void public_key_free(struct public_key *key)
> {
> if (key) {
> kfree(key->key);
> + kfree(key->params);
> kfree(key);
> }
> }
> @@ -124,6 +125,11 @@ static int software_key_query(const struct kernel_pkey_params *params,
> if (ret < 0)
> goto error_free_tfm;
>
> + ret = crypto_akcipher_set_params(tfm, pkey->algo, pkey->params,
> + pkey->paramlen);
> + if (ret)
> + goto error_free_tfm;
> +
> len = crypto_akcipher_maxsize(tfm);
> info->key_size = len * 8;
> info->max_data_size = len;
> @@ -182,6 +188,11 @@ static int software_key_eds_op(struct kernel_pkey_params *params,
> if (ret)
> goto error_free_req;
>
> + ret = crypto_akcipher_set_params(tfm, pkey->algo, pkey->params,
> + pkey->paramlen);
> + if (ret)
> + goto error_free_req;
> +
> 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,
> @@ -263,6 +274,11 @@ int public_key_verify_signature(const struct public_key *pkey,
> if (ret)
> goto error_free_req;
>
> + ret = crypto_akcipher_set_params(tfm, pkey->algo, pkey->params,
> + pkey->paramlen);
> + if (ret)
> + goto error_free_req;
> +
> ret = -ENOMEM;
> outlen = crypto_akcipher_maxsize(tfm);
> output = kmalloc(outlen, GFP_KERNEL);
> diff --git a/crypto/asymmetric_keys/x509.asn1 b/crypto/asymmetric_keys/x509.asn1
> index aae0cde414e2..5c9f4e4a5231 100644
> --- a/crypto/asymmetric_keys/x509.asn1
> +++ b/crypto/asymmetric_keys/x509.asn1
> @@ -22,7 +22,7 @@ CertificateSerialNumber ::= INTEGER
>
> AlgorithmIdentifier ::= SEQUENCE {
> algorithm OBJECT IDENTIFIER ({ x509_note_OID }),
> - parameters ANY OPTIONAL
> + parameters ANY OPTIONAL ({ x509_note_params })
> }
>
> Name ::= SEQUENCE OF RelativeDistinguishedName
> diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
> index 991f4d735a4e..16d8936d143b 100644
> --- a/crypto/asymmetric_keys/x509_cert_parser.c
> +++ b/crypto/asymmetric_keys/x509_cert_parser.c
> @@ -26,6 +26,9 @@ struct x509_parse_context {
> 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 */
> @@ -109,6 +112,13 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
>
> 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)
> @@ -401,6 +411,23 @@ int x509_note_subject(void *context, size_t hdrlen,
> }
>
> /*
> + * Extract parameters for particular keys
> + */
> +int x509_note_params(void *context, size_t hdrlen,
> + unsigned char tag,
> + const void *value, size_t vlen)
> +{
> + struct x509_parse_context *ctx = context;
> +
> + if (ctx->last_oid != OID_gost2012PublicKey256 &&
> + ctx->last_oid != OID_gost2012PublicKey512)
> + return 0;
> + ctx->params = value;
> + ctx->params_size = vlen;
> + return 0;
> +}
> +
> +/*
> * Extract the data for the public key algorithm
> */
> int x509_extract_key_data(void *context, size_t hdrlen,

Missed one hunk, which set ctx->key_algo:

@@ -436,16 +449,21 @@ int x509_extract_key_data(void *context, size_t hdrlen,
{
struct x509_parse_context *ctx = context;

- if (ctx->last_oid != OID_rsaEncryption)
+ ctx->key_algo = ctx->last_oid;
+ if (ctx->last_oid == OID_rsaEncryption)
+ ctx->cert->pub->pkey_algo = "rsa";
+ else if (ctx->last_oid == OID_gost2012PublicKey256 ||
+ ctx->last_oid == OID_gost2012PublicKey512)
+ ctx->cert->pub->pkey_algo = "ecrdsa";
+ else
return -ENOPKG;

- ctx->cert->pub->pkey_algo = "rsa";
-
/* 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;
}

> diff --git a/crypto/testmgr.c b/crypto/testmgr.c
> index 0f684a414acb..a030526a6609 100644
> --- a/crypto/testmgr.c
> +++ b/crypto/testmgr.c
> @@ -2257,6 +2257,11 @@ static int test_akcipher_one(struct crypto_akcipher *tfm,
> if (err)
> goto free_req;
>
> + err = crypto_akcipher_set_params(tfm, vecs->algo, vecs->params,
> + vecs->param_len);
> + if (err)
> + goto free_req;
> +
> err = -ENOMEM;
> out_len_max = crypto_akcipher_maxsize(tfm);
> outbuf_enc = kzalloc(out_len_max, GFP_KERNEL);
> diff --git a/crypto/testmgr.h b/crypto/testmgr.h
> index e7e56a8febbc..9855da080eaf 100644
> --- a/crypto/testmgr.h
> +++ b/crypto/testmgr.h
> @@ -124,13 +124,16 @@ struct drbg_testvec {
>
> struct akcipher_testvec {
> const unsigned char *key;
> + const unsigned char *params;
> const unsigned char *m;
> const unsigned char *c;
> unsigned int key_len;
> + unsigned int param_len;
> unsigned int m_size;
> unsigned int c_size;
> bool public_key_vec;
> bool siggen_sigver_test;
> + enum OID algo;
> };
>
> struct kpp_testvec {
> diff --git a/include/crypto/akcipher.h b/include/crypto/akcipher.h
> index afac71119396..d6aba84ed2c4 100644
> --- a/include/crypto/akcipher.h
> +++ b/include/crypto/akcipher.h
> @@ -13,6 +13,7 @@
> #ifndef _CRYPTO_AKCIPHER_H
> #define _CRYPTO_AKCIPHER_H
> #include <linux/crypto.h>
> +#include <linux/oid_registry.h>
>
> /**
> * struct akcipher_request - public key request
> @@ -73,6 +74,9 @@ struct crypto_akcipher {
> * @set_priv_key: Function invokes the algorithm specific set private key
> * function, which knows how to decode and interpret
> * the BER encoded private key
> + * @set_params: Function invokes the algorithm specific set parameters
> + * function, which knows how to decode and interpret
> + * the BER encoded public key
> * @max_size: Function returns dest buffer size required for a given key.
> * @init: Initialize the cryptographic transformation object.
> * This function is used to initialize the cryptographic
> @@ -98,6 +102,8 @@ struct akcipher_alg {
> unsigned int keylen);
> int (*set_priv_key)(struct crypto_akcipher *tfm, const void *key,
> unsigned int keylen);
> + int (*set_params)(struct crypto_akcipher *tfm, enum OID algo,
> + const void *params, unsigned int paramlen);
> unsigned int (*max_size)(struct crypto_akcipher *tfm);
> int (*init)(struct crypto_akcipher *tfm);
> void (*exit)(struct crypto_akcipher *tfm);
> @@ -452,4 +458,31 @@ static inline int crypto_akcipher_set_priv_key(struct crypto_akcipher *tfm,
>
> return alg->set_priv_key(tfm, key, keylen);
> }
> +
> +/**
> + * crypto_akcipher_set_params() - Invoke set parameters operation
> + *
> + * Function invokes the algorithm specific set parameters function, which
> + * knows how to decode and interpret the encoded parameters
> + *
> + * @tfm: tfm handle
> + * @algo: OID of the key algorithm
> + * @params: BER encoded key parameters
> + * @paramlen: length of the parameters
> + *
> + * Return: zero on success; error code in case of error
> + */
> +static inline int crypto_akcipher_set_params(struct crypto_akcipher *tfm,
> + enum OID algo,
> + const void *params,
> + unsigned int paramlen)
> +{
> + struct akcipher_alg *alg = crypto_akcipher_alg(tfm);
> +
> + if (alg->set_params)
> + return alg->set_params(tfm, algo, params, paramlen);
> + if (!params || !paramlen)
> + return 0;
> + return -ENOTSUPP;
> +}
> #endif
> diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
> index be626eac9113..712fe1214b5f 100644
> --- a/include/crypto/public_key.h
> +++ b/include/crypto/public_key.h
> @@ -15,6 +15,7 @@
> #define _LINUX_PUBLIC_KEY_H
>
> #include <linux/keyctl.h>
> +#include <linux/oid_registry.h>
>
> /*
> * Cryptographic data for the public-key subtype of the asymmetric key type.
> @@ -25,6 +26,9 @@
> struct public_key {
> void *key;
> u32 keylen;
> + enum OID algo;
> + void *params;
> + u32 paramlen;
> bool key_is_private;
> const char *id_type;
> const char *pkey_algo;
> diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
> index d2fa9ca42e9a..e2e323fd4826 100644
> --- a/include/linux/oid_registry.h
> +++ b/include/linux/oid_registry.h
> @@ -93,6 +93,12 @@ enum OID {
> OID_authorityKeyIdentifier, /* 2.5.29.35 */
> OID_extKeyUsage, /* 2.5.29.37 */
>
> + /* EC-RDSA */
> + OID_gost2012PublicKey256, /* 1.2.643.7.1.1.1.1 */
> + OID_gost2012PublicKey512, /* 1.2.643.7.1.1.1.2 */
> + OID_gost2012Signature256, /* 1.2.643.7.1.1.3.2 */
> + OID_gost2012Signature512, /* 1.2.643.7.1.1.3.3 */
> +
> OID__NR
> };
>
> --
> 2.11.0