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