Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 802c7f1c authored by Salvatore Benedetto's avatar Salvatore Benedetto Committed by Herbert Xu
Browse files

crypto: dh - Add DH software implementation



* Implement MPI based Diffie-Hellman under kpp API
 * Test provided uses data generad by OpenSSL

Signed-off-by: default avatarSalvatore Benedetto <salvatore.benedetto@intel.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 4e5f2c40
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -111,6 +111,14 @@ config CRYPTO_RSA
	help
	  Generic implementation of the RSA public key algorithm.

config CRYPTO_DH
	tristate "Diffie-Hellman algorithm"
	select CRYPTO_KPP
	select MPILIB
	help
	  Generic implementation of the Diffie-Hellman algorithm.


config CRYPTO_MANAGER
	tristate "Cryptographic algorithm manager"
	select CRYPTO_MANAGER2
+4 −0
Original line number Diff line number Diff line
@@ -32,6 +32,10 @@ obj-$(CONFIG_CRYPTO_HASH2) += crypto_hash.o
obj-$(CONFIG_CRYPTO_AKCIPHER2) += akcipher.o
obj-$(CONFIG_CRYPTO_KPP2) += kpp.o

dh_generic-y := dh.o
dh_generic-y += dh_helper.o
obj-$(CONFIG_CRYPTO_DH) += dh_generic.o

$(obj)/rsapubkey-asn1.o: $(obj)/rsapubkey-asn1.c $(obj)/rsapubkey-asn1.h
$(obj)/rsaprivkey-asn1.o: $(obj)/rsaprivkey-asn1.c $(obj)/rsaprivkey-asn1.h
clean-files += rsapubkey-asn1.c rsapubkey-asn1.h

crypto/dh.c

0 → 100644
+189 −0
Original line number Diff line number Diff line
/*  Diffie-Hellman Key Agreement Method [RFC2631]
 *
 * Copyright (c) 2016, Intel Corporation
 * Authors: Salvatore Benedetto <salvatore.benedetto@intel.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 <linux/module.h>
#include <crypto/internal/kpp.h>
#include <crypto/kpp.h>
#include <crypto/dh.h>
#include <linux/mpi.h>

struct dh_ctx {
	MPI p;
	MPI g;
	MPI xa;
};

static inline void dh_clear_params(struct dh_ctx *ctx)
{
	mpi_free(ctx->p);
	mpi_free(ctx->g);
	ctx->p = NULL;
	ctx->g = NULL;
}

static void dh_free_ctx(struct dh_ctx *ctx)
{
	dh_clear_params(ctx);
	mpi_free(ctx->xa);
	ctx->xa = NULL;
}

/*
 * If base is g we compute the public key
 *	ya = g^xa mod p; [RFC2631 sec 2.1.1]
 * else if base if the counterpart public key we compute the shared secret
 *	ZZ = yb^xa mod p; [RFC2631 sec 2.1.1]
 */
static int _compute_val(const struct dh_ctx *ctx, MPI base, MPI val)
{
	/* val = base^xa mod p */
	return mpi_powm(val, base, ctx->xa, ctx->p);
}

static inline struct dh_ctx *dh_get_ctx(struct crypto_kpp *tfm)
{
	return kpp_tfm_ctx(tfm);
}

static int dh_check_params_length(unsigned int p_len)
{
	return (p_len < 1536) ? -EINVAL : 0;
}

static int dh_set_params(struct dh_ctx *ctx, struct dh *params)
{
	if (unlikely(!params->p || !params->g))
		return -EINVAL;

	if (dh_check_params_length(params->p_size << 3))
		return -EINVAL;

	ctx->p = mpi_read_raw_data(params->p, params->p_size);
	if (!ctx->p)
		return -EINVAL;

	ctx->g = mpi_read_raw_data(params->g, params->g_size);
	if (!ctx->g) {
		mpi_free(ctx->p);
		return -EINVAL;
	}

	return 0;
}

static int dh_set_secret(struct crypto_kpp *tfm, void *buf, unsigned int len)
{
	struct dh_ctx *ctx = dh_get_ctx(tfm);
	struct dh params;

	if (crypto_dh_decode_key(buf, len, &params) < 0)
		return -EINVAL;

	if (dh_set_params(ctx, &params) < 0)
		return -EINVAL;

	ctx->xa = mpi_read_raw_data(params.key, params.key_size);
	if (!ctx->xa) {
		dh_clear_params(ctx);
		return -EINVAL;
	}

	return 0;
}

static int dh_compute_value(struct kpp_request *req)
{
	struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
	struct dh_ctx *ctx = dh_get_ctx(tfm);
	MPI base, val = mpi_alloc(0);
	int ret = 0;
	int sign;

	if (!val)
		return -ENOMEM;

	if (unlikely(!ctx->xa)) {
		ret = -EINVAL;
		goto err_free_val;
	}

	if (req->src) {
		base = mpi_read_raw_from_sgl(req->src, req->src_len);
		if (!base) {
			ret = EINVAL;
			goto err_free_val;
		}
	} else {
		base = ctx->g;
	}

	ret = _compute_val(ctx, base, val);
	if (ret)
		goto err_free_base;

	ret = mpi_write_to_sgl(val, req->dst, &req->dst_len, &sign);
	if (ret)
		goto err_free_base;

	if (sign < 0)
		ret = -EBADMSG;
err_free_base:
	if (req->src)
		mpi_free(base);
err_free_val:
	mpi_free(val);
	return ret;
}

static int dh_max_size(struct crypto_kpp *tfm)
{
	struct dh_ctx *ctx = dh_get_ctx(tfm);

	return mpi_get_size(ctx->p);
}

static void dh_exit_tfm(struct crypto_kpp *tfm)
{
	struct dh_ctx *ctx = dh_get_ctx(tfm);

	dh_free_ctx(ctx);
}

static struct kpp_alg dh = {
	.set_secret = dh_set_secret,
	.generate_public_key = dh_compute_value,
	.compute_shared_secret = dh_compute_value,
	.max_size = dh_max_size,
	.exit = dh_exit_tfm,
	.base = {
		.cra_name = "dh",
		.cra_driver_name = "dh-generic",
		.cra_priority = 100,
		.cra_module = THIS_MODULE,
		.cra_ctxsize = sizeof(struct dh_ctx),
	},
};

static int dh_init(void)
{
	return crypto_register_kpp(&dh);
}

static void dh_exit(void)
{
	crypto_unregister_kpp(&dh);
}

module_init(dh_init);
module_exit(dh_exit);
MODULE_ALIAS_CRYPTO("dh");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DH generic algorithm");

crypto/dh_helper.c

0 → 100644
+95 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2016, Intel Corporation
 * Authors: Salvatore Benedetto <salvatore.benedetto@intel.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 <linux/kernel.h>
#include <linux/export.h>
#include <linux/err.h>
#include <linux/string.h>
#include <crypto/dh.h>
#include <crypto/kpp.h>

#define DH_KPP_SECRET_MIN_SIZE (sizeof(struct kpp_secret) + 3 * sizeof(int))

static inline u8 *dh_pack_data(void *dst, const void *src, size_t size)
{
	memcpy(dst, src, size);
	return dst + size;
}

static inline const u8 *dh_unpack_data(void *dst, const void *src, size_t size)
{
	memcpy(dst, src, size);
	return src + size;
}

static inline int dh_data_size(const struct dh *p)
{
	return p->key_size + p->p_size + p->g_size;
}

int crypto_dh_key_len(const struct dh *p)
{
	return DH_KPP_SECRET_MIN_SIZE + dh_data_size(p);
}
EXPORT_SYMBOL_GPL(crypto_dh_key_len);

int crypto_dh_encode_key(char *buf, unsigned int len, const struct dh *params)
{
	u8 *ptr = buf;
	struct kpp_secret secret = {
		.type = CRYPTO_KPP_SECRET_TYPE_DH,
		.len = len
	};

	if (unlikely(!buf))
		return -EINVAL;

	if (len != crypto_dh_key_len(params))
		return -EINVAL;

	ptr = dh_pack_data(ptr, &secret, sizeof(secret));
	ptr = dh_pack_data(ptr, &params->key_size, sizeof(params->key_size));
	ptr = dh_pack_data(ptr, &params->p_size, sizeof(params->p_size));
	ptr = dh_pack_data(ptr, &params->g_size, sizeof(params->g_size));
	ptr = dh_pack_data(ptr, params->key, params->key_size);
	ptr = dh_pack_data(ptr, params->p, params->p_size);
	dh_pack_data(ptr, params->g, params->g_size);

	return 0;
}
EXPORT_SYMBOL_GPL(crypto_dh_encode_key);

int crypto_dh_decode_key(const char *buf, unsigned int len, struct dh *params)
{
	const u8 *ptr = buf;
	struct kpp_secret secret;

	if (unlikely(!buf || len < DH_KPP_SECRET_MIN_SIZE))
		return -EINVAL;

	ptr = dh_unpack_data(&secret, ptr, sizeof(secret));
	if (secret.type != CRYPTO_KPP_SECRET_TYPE_DH)
		return -EINVAL;

	ptr = dh_unpack_data(&params->key_size, ptr, sizeof(params->key_size));
	ptr = dh_unpack_data(&params->p_size, ptr, sizeof(params->p_size));
	ptr = dh_unpack_data(&params->g_size, ptr, sizeof(params->g_size));
	if (secret.len != crypto_dh_key_len(params))
		return -EINVAL;

	/* Don't allocate memory. Set pointers to data within
	 * the given buffer
	 */
	params->key = (void *)ptr;
	params->p = (void *)(ptr + params->key_size);
	params->g = (void *)(ptr + params->key_size + params->p_size);

	return 0;
}
EXPORT_SYMBOL_GPL(crypto_dh_decode_key);
+144 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include <crypto/rng.h>
#include <crypto/drbg.h>
#include <crypto/akcipher.h>
#include <crypto/kpp.h>

#include "internal.h"

@@ -120,6 +121,11 @@ struct akcipher_test_suite {
	unsigned int count;
};

struct kpp_test_suite {
	struct kpp_testvec *vecs;
	unsigned int count;
};

struct alg_test_desc {
	const char *alg;
	int (*test)(const struct alg_test_desc *desc, const char *driver,
@@ -134,6 +140,7 @@ struct alg_test_desc {
		struct cprng_test_suite cprng;
		struct drbg_test_suite drbg;
		struct akcipher_test_suite akcipher;
		struct kpp_test_suite kpp;
	} suite;
};

@@ -1777,6 +1784,133 @@ static int alg_test_drbg(const struct alg_test_desc *desc, const char *driver,

}

static int do_test_kpp(struct crypto_kpp *tfm, struct kpp_testvec *vec,
		       const char *alg)
{
	struct kpp_request *req;
	void *input_buf = NULL;
	void *output_buf = NULL;
	struct tcrypt_result result;
	unsigned int out_len_max;
	int err = -ENOMEM;
	struct scatterlist src, dst;

	req = kpp_request_alloc(tfm, GFP_KERNEL);
	if (!req)
		return err;

	init_completion(&result.completion);

	err = crypto_kpp_set_secret(tfm, vec->secret, vec->secret_size);
	if (err < 0)
		goto free_req;

	out_len_max = crypto_kpp_maxsize(tfm);
	output_buf = kzalloc(out_len_max, GFP_KERNEL);
	if (!output_buf) {
		err = -ENOMEM;
		goto free_req;
	}

	/* Use appropriate parameter as base */
	kpp_request_set_input(req, NULL, 0);
	sg_init_one(&dst, output_buf, out_len_max);
	kpp_request_set_output(req, &dst, out_len_max);
	kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
				 tcrypt_complete, &result);

	/* Compute public key */
	err = wait_async_op(&result, crypto_kpp_generate_public_key(req));
	if (err) {
		pr_err("alg: %s: generate public key test failed. err %d\n",
		       alg, err);
		goto free_output;
	}
	/* Verify calculated public key */
	if (memcmp(vec->expected_a_public, sg_virt(req->dst),
		   vec->expected_a_public_size)) {
		pr_err("alg: %s: generate public key test failed. Invalid output\n",
		       alg);
		err = -EINVAL;
		goto free_output;
	}

	/* Calculate shared secret key by using counter part (b) public key. */
	input_buf = kzalloc(vec->b_public_size, GFP_KERNEL);
	if (!input_buf) {
		err = -ENOMEM;
		goto free_output;
	}

	memcpy(input_buf, vec->b_public, vec->b_public_size);
	sg_init_one(&src, input_buf, vec->b_public_size);
	sg_init_one(&dst, output_buf, out_len_max);
	kpp_request_set_input(req, &src, vec->b_public_size);
	kpp_request_set_output(req, &dst, out_len_max);
	kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
				 tcrypt_complete, &result);
	err = wait_async_op(&result, crypto_kpp_compute_shared_secret(req));
	if (err) {
		pr_err("alg: %s: compute shard secret test failed. err %d\n",
		       alg, err);
		goto free_all;
	}
	/*
	 * verify shared secret from which the user will derive
	 * secret key by executing whatever hash it has chosen
	 */
	if (memcmp(vec->expected_ss, sg_virt(req->dst),
		   vec->expected_ss_size)) {
		pr_err("alg: %s: compute shared secret test failed. Invalid output\n",
		       alg);
		err = -EINVAL;
	}

free_all:
	kfree(input_buf);
free_output:
	kfree(output_buf);
free_req:
	kpp_request_free(req);
	return err;
}

static int test_kpp(struct crypto_kpp *tfm, const char *alg,
		    struct kpp_testvec *vecs, unsigned int tcount)
{
	int ret, i;

	for (i = 0; i < tcount; i++) {
		ret = do_test_kpp(tfm, vecs++, alg);
		if (ret) {
			pr_err("alg: %s: test failed on vector %d, err=%d\n",
			       alg, i + 1, ret);
			return ret;
		}
	}
	return 0;
}

static int alg_test_kpp(const struct alg_test_desc *desc, const char *driver,
			u32 type, u32 mask)
{
	struct crypto_kpp *tfm;
	int err = 0;

	tfm = crypto_alloc_kpp(driver, type | CRYPTO_ALG_INTERNAL, mask);
	if (IS_ERR(tfm)) {
		pr_err("alg: kpp: Failed to load tfm for %s: %ld\n",
		       driver, PTR_ERR(tfm));
		return PTR_ERR(tfm);
	}
	if (desc->suite.kpp.vecs)
		err = test_kpp(tfm, desc->alg, desc->suite.kpp.vecs,
			       desc->suite.kpp.count);

	crypto_free_kpp(tfm);
	return err;
}

static int do_test_rsa(struct crypto_akcipher *tfm,
		       struct akcipher_testvec *vecs)
{
@@ -2728,6 +2862,16 @@ static const struct alg_test_desc alg_test_descs[] = {
				}
			}
		}
	}, {
		.alg = "dh",
		.test = alg_test_kpp,
		.fips_allowed = 1,
		.suite = {
			.kpp = {
				.vecs = dh_tv_template,
				.count = DH_TEST_VECTORS
			}
		}
	}, {
		.alg = "digest_null",
		.test = alg_test_null,
Loading