Skip to content

Commit e3fe0ae

Browse files
smuellerDDherbertx
authored andcommitted
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>
1 parent 73463ad commit e3fe0ae

File tree

3 files changed

+79
-6
lines changed

3 files changed

+79
-6
lines changed

crypto/dh.c

+63-3
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@
1616
#include <linux/mpi.h>
1717

1818
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. */
2223
};
2324

2425
static void dh_clear_ctx(struct dh_ctx *ctx)
2526
{
2627
mpi_free(ctx->p);
28+
mpi_free(ctx->q);
2729
mpi_free(ctx->g);
2830
mpi_free(ctx->xa);
2931
memset(ctx, 0, sizeof(*ctx));
@@ -60,6 +62,12 @@ static int dh_set_params(struct dh_ctx *ctx, struct dh *params)
6062
if (!ctx->p)
6163
return -EINVAL;
6264

65+
if (params->q && params->q_size) {
66+
ctx->q = mpi_read_raw_data(params->q, params->q_size);
67+
if (!ctx->q)
68+
return -EINVAL;
69+
}
70+
6371
ctx->g = mpi_read_raw_data(params->g, params->g_size);
6472
if (!ctx->g)
6573
return -EINVAL;
@@ -93,6 +101,55 @@ static int dh_set_secret(struct crypto_kpp *tfm, const void *buf,
93101
return -EINVAL;
94102
}
95103

104+
/*
105+
* SP800-56A public key verification:
106+
*
107+
* * If Q is provided as part of the domain paramenters, a full validation
108+
* according to SP800-56A section 5.6.2.3.1 is performed.
109+
*
110+
* * If Q is not provided, a partial validation according to SP800-56A section
111+
* 5.6.2.3.2 is performed.
112+
*/
113+
static int dh_is_pubkey_valid(struct dh_ctx *ctx, MPI y)
114+
{
115+
if (unlikely(!ctx->p))
116+
return -EINVAL;
117+
118+
/*
119+
* Step 1: Verify that 2 <= y <= p - 2.
120+
*
121+
* The upper limit check is actually y < p instead of y < p - 1
122+
* as the mpi_sub_ui function is yet missing.
123+
*/
124+
if (mpi_cmp_ui(y, 1) < 1 || mpi_cmp(y, ctx->p) >= 0)
125+
return -EINVAL;
126+
127+
/* Step 2: Verify that 1 = y^q mod p */
128+
if (ctx->q) {
129+
MPI val = mpi_alloc(0);
130+
int ret;
131+
132+
if (!val)
133+
return -ENOMEM;
134+
135+
ret = mpi_powm(val, y, ctx->q, ctx->p);
136+
137+
if (ret) {
138+
mpi_free(val);
139+
return ret;
140+
}
141+
142+
ret = mpi_cmp_ui(val, 1);
143+
144+
mpi_free(val);
145+
146+
if (ret != 0)
147+
return -EINVAL;
148+
}
149+
150+
return 0;
151+
}
152+
96153
static int dh_compute_value(struct kpp_request *req)
97154
{
98155
struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
@@ -115,6 +172,9 @@ static int dh_compute_value(struct kpp_request *req)
115172
ret = -EINVAL;
116173
goto err_free_val;
117174
}
175+
ret = dh_is_pubkey_valid(ctx, base);
176+
if (ret)
177+
goto err_free_val;
118178
} else {
119179
base = ctx->g;
120180
}

crypto/dh_helper.c

+12-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ static inline const u8 *dh_unpack_data(void *dst, const void *src, size_t size)
3030

3131
static inline unsigned int dh_data_size(const struct dh *p)
3232
{
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;
3434
}
3535

3636
unsigned int crypto_dh_key_len(const struct dh *p)
@@ -56,9 +56,11 @@ int crypto_dh_encode_key(char *buf, unsigned int len, const struct dh *params)
5656
ptr = dh_pack_data(ptr, &secret, sizeof(secret));
5757
ptr = dh_pack_data(ptr, &params->key_size, sizeof(params->key_size));
5858
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));
5960
ptr = dh_pack_data(ptr, &params->g_size, sizeof(params->g_size));
6061
ptr = dh_pack_data(ptr, params->key, params->key_size);
6162
ptr = dh_pack_data(ptr, params->p, params->p_size);
63+
ptr = dh_pack_data(ptr, params->q, params->q_size);
6264
dh_pack_data(ptr, params->g, params->g_size);
6365

6466
return 0;
@@ -79,6 +81,7 @@ int crypto_dh_decode_key(const char *buf, unsigned int len, struct dh *params)
7981

8082
ptr = dh_unpack_data(&params->key_size, ptr, sizeof(params->key_size));
8183
ptr = dh_unpack_data(&params->p_size, ptr, sizeof(params->p_size));
84+
ptr = dh_unpack_data(&params->q_size, ptr, sizeof(params->q_size));
8285
ptr = dh_unpack_data(&params->g_size, ptr, sizeof(params->g_size));
8386
if (secret.len != crypto_dh_key_len(params))
8487
return -EINVAL;
@@ -88,15 +91,17 @@ int crypto_dh_decode_key(const char *buf, unsigned int len, struct dh *params)
8891
* some drivers assume otherwise.
8992
*/
9093
if (params->key_size > params->p_size ||
91-
params->g_size > params->p_size)
94+
params->g_size > params->p_size || params->q_size > params->p_size)
9295
return -EINVAL;
9396

9497
/* Don't allocate memory. Set pointers to data within
9598
* the given buffer
9699
*/
97100
params->key = (void *)ptr;
98101
params->p = (void *)(ptr + params->key_size);
99-
params->g = (void *)(ptr + params->key_size + params->p_size);
102+
params->q = (void *)(ptr + params->key_size + params->p_size);
103+
params->g = (void *)(ptr + params->key_size + params->p_size +
104+
params->q_size);
100105

101106
/*
102107
* Don't permit 'p' to be 0. It's not a prime number, and it's subject
@@ -106,6 +111,10 @@ int crypto_dh_decode_key(const char *buf, unsigned int len, struct dh *params)
106111
if (memchr_inv(params->p, 0, params->p_size) == NULL)
107112
return -EINVAL;
108113

114+
/* It is permissible to not provide Q. */
115+
if (params->q_size == 0)
116+
params->q = NULL;
117+
109118
return 0;
110119
}
111120
EXPORT_SYMBOL_GPL(crypto_dh_decode_key);

include/crypto/dh.h

+4
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,21 @@
2929
*
3030
* @key: Private DH key
3131
* @p: Diffie-Hellman parameter P
32+
* @q: Diffie-Hellman parameter Q
3233
* @g: Diffie-Hellman generator G
3334
* @key_size: Size of the private DH key
3435
* @p_size: Size of DH parameter P
36+
* @q_size: Size of DH parameter Q
3537
* @g_size: Size of DH generator G
3638
*/
3739
struct dh {
3840
void *key;
3941
void *p;
42+
void *q;
4043
void *g;
4144
unsigned int key_size;
4245
unsigned int p_size;
46+
unsigned int q_size;
4347
unsigned int g_size;
4448
};
4549

0 commit comments

Comments
 (0)