Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

crypto: dh - add public key verification test

According to SP800-56A section 5.6.2.1, the public key to be processed
for the DH operation shall be checked for appropriateness. The check
shall covers the full verification test in case the domain parameter Q
is provided as defined in SP800-56A section 5.6.2.3.1. If Q is not
provided, the partial check according to SP800-56A section 5.6.2.3.2 is
performed.

The full verification test requires the presence of the domain parameter
Q. Thus, the patch adds the support to handle Q. It is permissible to
not provide the Q value as part of the domain parameters. This implies
that the interface is still backwards-compatible where so far only P and
G are to be provided. However, if Q is provided, it is imported.

Without the test, the NIST ACVP testing fails. After adding this check,
the NIST ACVP testing passes. Testing without providing the Q domain
parameter has been performed to verify the interface has not changed.

Signed-off-by: Stephan Mueller <smueller@chronox.de>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

authored by

Stephan Mueller and committed by
Herbert Xu
e3fe0ae1 73463ade

+79 -6
+63 -3
crypto/dh.c
··· 16 16 #include <linux/mpi.h> 17 17 18 18 struct dh_ctx { 19 - MPI p; 20 - MPI g; 21 - MPI xa; 19 + MPI p; /* Value is guaranteed to be set. */ 20 + MPI q; /* Value is optional. */ 21 + MPI g; /* Value is guaranteed to be set. */ 22 + MPI xa; /* Value is guaranteed to be set. */ 22 23 }; 23 24 24 25 static void dh_clear_ctx(struct dh_ctx *ctx) 25 26 { 26 27 mpi_free(ctx->p); 28 + mpi_free(ctx->q); 27 29 mpi_free(ctx->g); 28 30 mpi_free(ctx->xa); 29 31 memset(ctx, 0, sizeof(*ctx)); ··· 62 60 if (!ctx->p) 63 61 return -EINVAL; 64 62 63 + if (params->q && params->q_size) { 64 + ctx->q = mpi_read_raw_data(params->q, params->q_size); 65 + if (!ctx->q) 66 + return -EINVAL; 67 + } 68 + 65 69 ctx->g = mpi_read_raw_data(params->g, params->g_size); 66 70 if (!ctx->g) 67 71 return -EINVAL; ··· 101 93 return -EINVAL; 102 94 } 103 95 96 + /* 97 + * SP800-56A public key verification: 98 + * 99 + * * If Q is provided as part of the domain paramenters, a full validation 100 + * according to SP800-56A section 5.6.2.3.1 is performed. 101 + * 102 + * * If Q is not provided, a partial validation according to SP800-56A section 103 + * 5.6.2.3.2 is performed. 104 + */ 105 + static int dh_is_pubkey_valid(struct dh_ctx *ctx, MPI y) 106 + { 107 + if (unlikely(!ctx->p)) 108 + return -EINVAL; 109 + 110 + /* 111 + * Step 1: Verify that 2 <= y <= p - 2. 112 + * 113 + * The upper limit check is actually y < p instead of y < p - 1 114 + * as the mpi_sub_ui function is yet missing. 115 + */ 116 + if (mpi_cmp_ui(y, 1) < 1 || mpi_cmp(y, ctx->p) >= 0) 117 + return -EINVAL; 118 + 119 + /* Step 2: Verify that 1 = y^q mod p */ 120 + if (ctx->q) { 121 + MPI val = mpi_alloc(0); 122 + int ret; 123 + 124 + if (!val) 125 + return -ENOMEM; 126 + 127 + ret = mpi_powm(val, y, ctx->q, ctx->p); 128 + 129 + if (ret) { 130 + mpi_free(val); 131 + return ret; 132 + } 133 + 134 + ret = mpi_cmp_ui(val, 1); 135 + 136 + mpi_free(val); 137 + 138 + if (ret != 0) 139 + return -EINVAL; 140 + } 141 + 142 + return 0; 143 + } 144 + 104 145 static int dh_compute_value(struct kpp_request *req) 105 146 { 106 147 struct crypto_kpp *tfm = crypto_kpp_reqtfm(req); ··· 172 115 ret = -EINVAL; 173 116 goto err_free_val; 174 117 } 118 + ret = dh_is_pubkey_valid(ctx, base); 119 + if (ret) 120 + goto err_free_val; 175 121 } else { 176 122 base = ctx->g; 177 123 }
+12 -3
crypto/dh_helper.c
··· 30 30 31 31 static inline unsigned int dh_data_size(const struct dh *p) 32 32 { 33 - return p->key_size + p->p_size + p->g_size; 33 + return p->key_size + p->p_size + p->q_size + p->g_size; 34 34 } 35 35 36 36 unsigned int crypto_dh_key_len(const struct dh *p) ··· 56 56 ptr = dh_pack_data(ptr, &secret, sizeof(secret)); 57 57 ptr = dh_pack_data(ptr, &params->key_size, sizeof(params->key_size)); 58 58 ptr = dh_pack_data(ptr, &params->p_size, sizeof(params->p_size)); 59 + ptr = dh_pack_data(ptr, &params->q_size, sizeof(params->q_size)); 59 60 ptr = dh_pack_data(ptr, &params->g_size, sizeof(params->g_size)); 60 61 ptr = dh_pack_data(ptr, params->key, params->key_size); 61 62 ptr = dh_pack_data(ptr, params->p, params->p_size); 63 + ptr = dh_pack_data(ptr, params->q, params->q_size); 62 64 dh_pack_data(ptr, params->g, params->g_size); 63 65 64 66 return 0; ··· 81 79 82 80 ptr = dh_unpack_data(&params->key_size, ptr, sizeof(params->key_size)); 83 81 ptr = dh_unpack_data(&params->p_size, ptr, sizeof(params->p_size)); 82 + ptr = dh_unpack_data(&params->q_size, ptr, sizeof(params->q_size)); 84 83 ptr = dh_unpack_data(&params->g_size, ptr, sizeof(params->g_size)); 85 84 if (secret.len != crypto_dh_key_len(params)) 86 85 return -EINVAL; ··· 91 88 * some drivers assume otherwise. 92 89 */ 93 90 if (params->key_size > params->p_size || 94 - params->g_size > params->p_size) 91 + params->g_size > params->p_size || params->q_size > params->p_size) 95 92 return -EINVAL; 96 93 97 94 /* Don't allocate memory. Set pointers to data within ··· 99 96 */ 100 97 params->key = (void *)ptr; 101 98 params->p = (void *)(ptr + params->key_size); 102 - params->g = (void *)(ptr + params->key_size + params->p_size); 99 + params->q = (void *)(ptr + params->key_size + params->p_size); 100 + params->g = (void *)(ptr + params->key_size + params->p_size + 101 + params->q_size); 103 102 104 103 /* 105 104 * Don't permit 'p' to be 0. It's not a prime number, and it's subject ··· 110 105 */ 111 106 if (memchr_inv(params->p, 0, params->p_size) == NULL) 112 107 return -EINVAL; 108 + 109 + /* It is permissible to not provide Q. */ 110 + if (params->q_size == 0) 111 + params->q = NULL; 113 112 114 113 return 0; 115 114 }
+4
include/crypto/dh.h
··· 29 29 * 30 30 * @key: Private DH key 31 31 * @p: Diffie-Hellman parameter P 32 + * @q: Diffie-Hellman parameter Q 32 33 * @g: Diffie-Hellman generator G 33 34 * @key_size: Size of the private DH key 34 35 * @p_size: Size of DH parameter P 36 + * @q_size: Size of DH parameter Q 35 37 * @g_size: Size of DH generator G 36 38 */ 37 39 struct dh { 38 40 void *key; 39 41 void *p; 42 + void *q; 40 43 void *g; 41 44 unsigned int key_size; 42 45 unsigned int p_size; 46 + unsigned int q_size; 43 47 unsigned int g_size; 44 48 }; 45 49