linux/crypto/dh.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*  Diffie-Hellman Key Agreement Method [RFC2631]
   3 *
   4 * Copyright (c) 2016, Intel Corporation
   5 * Authors: Salvatore Benedetto <salvatore.benedetto@intel.com>
   6 */
   7
   8#include <linux/module.h>
   9#include <crypto/internal/kpp.h>
  10#include <crypto/kpp.h>
  11#include <crypto/dh.h>
  12#include <linux/mpi.h>
  13
  14struct dh_ctx {
  15        MPI p;  /* Value is guaranteed to be set. */
  16        MPI q;  /* Value is optional. */
  17        MPI g;  /* Value is guaranteed to be set. */
  18        MPI xa; /* Value is guaranteed to be set. */
  19};
  20
  21static void dh_clear_ctx(struct dh_ctx *ctx)
  22{
  23        mpi_free(ctx->p);
  24        mpi_free(ctx->q);
  25        mpi_free(ctx->g);
  26        mpi_free(ctx->xa);
  27        memset(ctx, 0, sizeof(*ctx));
  28}
  29
  30/*
  31 * If base is g we compute the public key
  32 *      ya = g^xa mod p; [RFC2631 sec 2.1.1]
  33 * else if base if the counterpart public key we compute the shared secret
  34 *      ZZ = yb^xa mod p; [RFC2631 sec 2.1.1]
  35 */
  36static int _compute_val(const struct dh_ctx *ctx, MPI base, MPI val)
  37{
  38        /* val = base^xa mod p */
  39        return mpi_powm(val, base, ctx->xa, ctx->p);
  40}
  41
  42static inline struct dh_ctx *dh_get_ctx(struct crypto_kpp *tfm)
  43{
  44        return kpp_tfm_ctx(tfm);
  45}
  46
  47static int dh_check_params_length(unsigned int p_len)
  48{
  49        return (p_len < 1536) ? -EINVAL : 0;
  50}
  51
  52static int dh_set_params(struct dh_ctx *ctx, struct dh *params)
  53{
  54        if (dh_check_params_length(params->p_size << 3))
  55                return -EINVAL;
  56
  57        ctx->p = mpi_read_raw_data(params->p, params->p_size);
  58        if (!ctx->p)
  59                return -EINVAL;
  60
  61        if (params->q && params->q_size) {
  62                ctx->q = mpi_read_raw_data(params->q, params->q_size);
  63                if (!ctx->q)
  64                        return -EINVAL;
  65        }
  66
  67        ctx->g = mpi_read_raw_data(params->g, params->g_size);
  68        if (!ctx->g)
  69                return -EINVAL;
  70
  71        return 0;
  72}
  73
  74static int dh_set_secret(struct crypto_kpp *tfm, const void *buf,
  75                         unsigned int len)
  76{
  77        struct dh_ctx *ctx = dh_get_ctx(tfm);
  78        struct dh params;
  79
  80        /* Free the old MPI key if any */
  81        dh_clear_ctx(ctx);
  82
  83        if (crypto_dh_decode_key(buf, len, &params) < 0)
  84                goto err_clear_ctx;
  85
  86        if (dh_set_params(ctx, &params) < 0)
  87                goto err_clear_ctx;
  88
  89        ctx->xa = mpi_read_raw_data(params.key, params.key_size);
  90        if (!ctx->xa)
  91                goto err_clear_ctx;
  92
  93        return 0;
  94
  95err_clear_ctx:
  96        dh_clear_ctx(ctx);
  97        return -EINVAL;
  98}
  99
 100/*
 101 * SP800-56A public key verification:
 102 *
 103 * * If Q is provided as part of the domain paramenters, a full validation
 104 *   according to SP800-56A section 5.6.2.3.1 is performed.
 105 *
 106 * * If Q is not provided, a partial validation according to SP800-56A section
 107 *   5.6.2.3.2 is performed.
 108 */
 109static int dh_is_pubkey_valid(struct dh_ctx *ctx, MPI y)
 110{
 111        if (unlikely(!ctx->p))
 112                return -EINVAL;
 113
 114        /*
 115         * Step 1: Verify that 2 <= y <= p - 2.
 116         *
 117         * The upper limit check is actually y < p instead of y < p - 1
 118         * as the mpi_sub_ui function is yet missing.
 119         */
 120        if (mpi_cmp_ui(y, 1) < 1 || mpi_cmp(y, ctx->p) >= 0)
 121                return -EINVAL;
 122
 123        /* Step 2: Verify that 1 = y^q mod p */
 124        if (ctx->q) {
 125                MPI val = mpi_alloc(0);
 126                int ret;
 127
 128                if (!val)
 129                        return -ENOMEM;
 130
 131                ret = mpi_powm(val, y, ctx->q, ctx->p);
 132
 133                if (ret) {
 134                        mpi_free(val);
 135                        return ret;
 136                }
 137
 138                ret = mpi_cmp_ui(val, 1);
 139
 140                mpi_free(val);
 141
 142                if (ret != 0)
 143                        return -EINVAL;
 144        }
 145
 146        return 0;
 147}
 148
 149static int dh_compute_value(struct kpp_request *req)
 150{
 151        struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
 152        struct dh_ctx *ctx = dh_get_ctx(tfm);
 153        MPI base, val = mpi_alloc(0);
 154        int ret = 0;
 155        int sign;
 156
 157        if (!val)
 158                return -ENOMEM;
 159
 160        if (unlikely(!ctx->xa)) {
 161                ret = -EINVAL;
 162                goto err_free_val;
 163        }
 164
 165        if (req->src) {
 166                base = mpi_read_raw_from_sgl(req->src, req->src_len);
 167                if (!base) {
 168                        ret = -EINVAL;
 169                        goto err_free_val;
 170                }
 171                ret = dh_is_pubkey_valid(ctx, base);
 172                if (ret)
 173                        goto err_free_base;
 174        } else {
 175                base = ctx->g;
 176        }
 177
 178        ret = _compute_val(ctx, base, val);
 179        if (ret)
 180                goto err_free_base;
 181
 182        ret = mpi_write_to_sgl(val, req->dst, req->dst_len, &sign);
 183        if (ret)
 184                goto err_free_base;
 185
 186        if (sign < 0)
 187                ret = -EBADMSG;
 188err_free_base:
 189        if (req->src)
 190                mpi_free(base);
 191err_free_val:
 192        mpi_free(val);
 193        return ret;
 194}
 195
 196static unsigned int dh_max_size(struct crypto_kpp *tfm)
 197{
 198        struct dh_ctx *ctx = dh_get_ctx(tfm);
 199
 200        return mpi_get_size(ctx->p);
 201}
 202
 203static void dh_exit_tfm(struct crypto_kpp *tfm)
 204{
 205        struct dh_ctx *ctx = dh_get_ctx(tfm);
 206
 207        dh_clear_ctx(ctx);
 208}
 209
 210static struct kpp_alg dh = {
 211        .set_secret = dh_set_secret,
 212        .generate_public_key = dh_compute_value,
 213        .compute_shared_secret = dh_compute_value,
 214        .max_size = dh_max_size,
 215        .exit = dh_exit_tfm,
 216        .base = {
 217                .cra_name = "dh",
 218                .cra_driver_name = "dh-generic",
 219                .cra_priority = 100,
 220                .cra_module = THIS_MODULE,
 221                .cra_ctxsize = sizeof(struct dh_ctx),
 222        },
 223};
 224
 225static int dh_init(void)
 226{
 227        return crypto_register_kpp(&dh);
 228}
 229
 230static void dh_exit(void)
 231{
 232        crypto_unregister_kpp(&dh);
 233}
 234
 235subsys_initcall(dh_init);
 236module_exit(dh_exit);
 237MODULE_ALIAS_CRYPTO("dh");
 238MODULE_LICENSE("GPL");
 239MODULE_DESCRIPTION("DH generic algorithm");
 240