Fix DH_get_nid() so that it does not cache values.
[openssl.git] / crypto / dh / dh_group_params.c
1 /*
2  * Copyright 2017-2020 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9
10 /* DH parameters from RFC7919 and RFC3526 */
11
12 /*
13  * DH low level APIs are deprecated for public use, but still ok for
14  * internal use.
15  */
16 #include "internal/deprecated.h"
17
18 #include <stdio.h>
19 #include "internal/cryptlib.h"
20 #include "internal/ffc.h"
21 #include "dh_local.h"
22 #include <openssl/bn.h>
23 #include <openssl/objects.h>
24 #include "crypto/bn_dh.h"
25 #include "crypto/dh.h"
26 #include "e_os.h" /* strcasecmp */
27
28 #define FFDHE(sz) {                                                            \
29     SN_ffdhe##sz, NID_ffdhe##sz,                                               \
30     sz,                                                                        \
31     &_bignum_ffdhe##sz##_p, &_bignum_ffdhe##sz##_q, &_bignum_const_2           \
32 }
33
34 #define MODP(sz)  {                                                            \
35     SN_modp_##sz, NID_modp_##sz,                                               \
36     sz,                                                                        \
37     &_bignum_modp_##sz##_p, &_bignum_modp_##sz##_q,  &_bignum_const_2          \
38 }
39
40 #define RFC5114(name, uid, sz, tag)  {                                         \
41     name, uid,                                                                 \
42     sz,                                                                        \
43     &_bignum_dh##tag##_p, &_bignum_dh##tag##_q, &_bignum_dh##tag##_g           \
44 }
45
46 typedef struct dh_named_group_st {
47     const char *name;
48     int uid;
49     int32_t nbits;
50     const BIGNUM *p;
51     const BIGNUM *q;
52     const BIGNUM *g;
53 } DH_NAMED_GROUP;
54
55
56 static const DH_NAMED_GROUP dh_named_groups[] = {
57     FFDHE(2048),
58     FFDHE(3072),
59     FFDHE(4096),
60     FFDHE(6144),
61     FFDHE(8192),
62 #ifndef FIPS_MODE
63     MODP(1536),
64 #endif
65     MODP(2048),
66     MODP(3072),
67     MODP(4096),
68     MODP(6144),
69     MODP(8192),
70     /*
71      * Additional dh named groups from RFC 5114 that have a different g.
72      * The uid can be any unique identifier.
73      */
74 #ifndef FIPS_MODE
75     RFC5114("dh_1024_160", 1, 1024, 1024_160),
76     RFC5114("dh_2048_224", 2, 2048, 2048_224),
77     RFC5114("dh_2048_256", 3, 2048, 2048_256),
78 #endif
79 };
80
81 int ffc_named_group_to_uid(const char *name)
82 {
83     size_t i;
84
85     for (i = 0; i < OSSL_NELEM(dh_named_groups); ++i) {
86         if (strcasecmp(dh_named_groups[i].name, name) == 0)
87             return dh_named_groups[i].uid;
88     }
89     return NID_undef;
90 }
91
92 const char *ffc_named_group_from_uid(int uid)
93 {
94     size_t i;
95
96     for (i = 0; i < OSSL_NELEM(dh_named_groups); ++i) {
97         if (dh_named_groups[i].uid == uid)
98             return dh_named_groups[i].name;
99     }
100     return NULL;
101 }
102
103 static DH *dh_param_init(OPENSSL_CTX *libctx, int uid, const BIGNUM *p,
104                          const BIGNUM *q, const BIGNUM *g)
105 {
106     DH *dh = dh_new_with_libctx(libctx);
107
108     if (dh == NULL)
109         return NULL;
110
111     dh->params.nid = uid;
112     dh->params.p = (BIGNUM *)p;
113     dh->params.q = (BIGNUM *)q;
114     dh->params.g = (BIGNUM *)g;
115     dh->length = BN_num_bits(q);
116     dh->dirty_cnt++;
117     return dh;
118 }
119
120 static DH *dh_new_by_group_name(OPENSSL_CTX *libctx, const char *name)
121 {
122     int i;
123
124     if (name == NULL)
125         return NULL;
126
127     for (i = 0; i < (int)OSSL_NELEM(dh_named_groups); ++i) {
128         if (strcasecmp(dh_named_groups[i].name, name) == 0) {
129             return dh_param_init(libctx, dh_named_groups[i].uid,
130                                  dh_named_groups[i].p,
131                                  dh_named_groups[i].q,
132                                  dh_named_groups[i].g);
133         }
134     }
135     DHerr(0, DH_R_INVALID_PARAMETER_NID);
136     return NULL;
137 }
138
139 DH *dh_new_by_nid_with_libctx(OPENSSL_CTX *libctx, int nid)
140 {
141     const char *name = ffc_named_group_from_uid(nid);
142
143     return dh_new_by_group_name(libctx, name);
144 }
145
146 DH *DH_new_by_nid(int nid)
147 {
148     return dh_new_by_nid_with_libctx(NULL, nid);
149 }
150
151 int ffc_set_group_pqg(FFC_PARAMS *ffc, const char *group_name)
152 {
153     int i;
154     BIGNUM *q = NULL;
155
156     if (ffc == NULL)
157         return 0;
158
159     for (i = 0; i < (int)OSSL_NELEM(dh_named_groups); ++i) {
160         if (strcasecmp(dh_named_groups[i].name, group_name) == 0) {
161             ffc_params_set0_pqg(ffc,
162                                 (BIGNUM *)dh_named_groups[i].p,
163                                 (BIGNUM *)dh_named_groups[i].q,
164                                 (BIGNUM *)dh_named_groups[i].g);
165             /* flush the cached nid, The DH layer is responsible for caching */
166             ffc->nid = NID_undef;
167             return 1;
168         }
169     }
170     /* gets here on error or if the name was not found */
171     BN_free(q);
172     return 0;
173 }
174
175 void dh_cache_named_group(DH *dh)
176 {
177     int i;
178
179     if (dh == NULL)
180         return;
181
182     dh->params.nid = NID_undef; /* flush cached value */
183
184     /* Exit if p or g is not set */
185     if (dh->params.p == NULL
186         || dh->params.g == NULL)
187         return;
188
189     for (i = 0; i < (int)OSSL_NELEM(dh_named_groups); ++i) {
190         /* Keep searching until a matching p and g is found */
191         if (BN_cmp(dh->params.p, dh_named_groups[i].p) == 0
192             && BN_cmp(dh->params.g, dh_named_groups[i].g) == 0) {
193                 /* Verify q is correct if it exists */
194                 if (dh->params.q != NULL) {
195                     if (BN_cmp(dh->params.q, dh_named_groups[i].q) != 0)
196                         continue;  /* ignore if q does not match */
197                 } else {
198                     dh->params.q = (BIGNUM *)dh_named_groups[i].q;
199                 }
200                 dh->params.nid = dh_named_groups[i].uid; /* cache the nid */
201                 dh->length = BN_num_bits(dh->params.q);
202                 dh->dirty_cnt++;
203                 break;
204         }
205     }
206 }
207
208 int DH_get_nid(const DH *dh)
209 {
210     if (dh == NULL)
211         return NID_undef;
212
213     return dh->params.nid;
214 }