Implement PKCS#3 DH Key Exchange in the default provider
[openssl.git] / providers / common / exchange / dh.c
1 /*
2  * Copyright 2019 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 #include <openssl/crypto.h>
11 #include <openssl/core_numbers.h>
12 #include <openssl/core_names.h>
13 #include <openssl/dh.h>
14 #include <openssl/params.h>
15 #include "internal/provider_algs.h"
16
17 static OSSL_OP_keyexch_newctx_fn dh_newctx;
18 static OSSL_OP_keyexch_init_fn dh_init;
19 static OSSL_OP_keyexch_set_peer_fn dh_set_peer;
20 static OSSL_OP_keyexch_derive_fn dh_derive;
21 static OSSL_OP_keyexch_freectx_fn dh_freectx;
22 static OSSL_OP_keyexch_dupctx_fn dh_dupctx;
23
24
25 typedef struct {
26     DH *dh;
27     DH *dhpeer;
28 } PROV_DH_CTX;
29
30 static void *dh_newctx(void *provctx)
31 {
32     return OPENSSL_zalloc(sizeof(PROV_DH_CTX));
33 }
34
35 static DH *param_to_dh(OSSL_PARAM params[], int priv)
36 {
37     DH *dh = DH_new();
38     OSSL_PARAM *paramptr;
39     BIGNUM *p = NULL, *g = NULL, *pub_key = NULL, *priv_key = NULL;
40
41     if (dh == NULL)
42         return NULL;
43
44     paramptr = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DH_P);
45     if (paramptr == NULL
46             || !OSSL_PARAM_get_BN(paramptr, &p))
47         goto err;
48
49     paramptr = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DH_G);
50     if (paramptr == NULL || !OSSL_PARAM_get_BN(paramptr, &g))
51         goto err;
52
53     if (!DH_set0_pqg(dh, p, NULL, g))
54         goto err;
55     p = g = NULL;
56
57     paramptr = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DH_PUB_KEY);
58     if (paramptr == NULL || !OSSL_PARAM_get_BN(paramptr, &pub_key))
59         goto err;
60
61     /* Private key is optional */
62     if (priv) {
63         paramptr = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DH_PRIV_KEY);
64         if (paramptr == NULL
65                 || (priv_key = BN_secure_new()) == NULL
66                 || !OSSL_PARAM_get_BN(paramptr, &priv_key))
67             goto err;
68     }
69
70     if (!DH_set0_key(dh, pub_key, priv_key))
71         goto err;
72
73     return dh;
74
75  err:
76     BN_free(p);
77     BN_free(g);
78     BN_free(pub_key);
79     BN_free(priv_key);
80     DH_free(dh);
81     return NULL;
82 }
83
84 static int dh_init(void *vpdhctx, OSSL_PARAM params[])
85 {
86     PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
87
88     DH_free(pdhctx->dh);
89     pdhctx->dh = param_to_dh(params, 1);
90
91     return pdhctx->dh != NULL;
92 }
93
94 static int dh_set_peer(void *vpdhctx, OSSL_PARAM params[])
95 {
96     PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
97
98     DH_free(pdhctx->dhpeer);
99     pdhctx->dhpeer = param_to_dh(params, 0);
100
101     return pdhctx->dhpeer != NULL;
102 }
103
104 static int dh_derive(void *vpdhctx, unsigned char *key, size_t *keylen,
105                      size_t outlen)
106 {
107     PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
108     int ret;
109     size_t dhsize;
110     const BIGNUM *pub_key = NULL;
111
112     /* TODO(3.0): Add errors to stack */
113     if (pdhctx->dh == NULL || pdhctx->dhpeer == NULL)
114         return 0;
115
116     dhsize = (size_t)DH_size(pdhctx->dh);
117     if (key == NULL) {
118         *keylen = dhsize;
119         return 1;
120     }
121     if (outlen < dhsize)
122         return 0;
123
124     DH_get0_key(pdhctx->dhpeer, &pub_key, NULL);
125     ret = DH_compute_key(key, pub_key, pdhctx->dh);
126     if (ret <= 0)
127         return 0;
128
129     *keylen = ret;
130     return 1;
131 }
132
133 static void dh_freectx(void *vpdhctx)
134 {
135     PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
136
137     DH_free(pdhctx->dh);
138     DH_free(pdhctx->dhpeer);
139
140     OPENSSL_free(pdhctx);
141 }
142
143 static void *dh_dupctx(void *vpdhctx)
144 {
145     PROV_DH_CTX *srcctx = (PROV_DH_CTX *)vpdhctx;
146     PROV_DH_CTX *dstctx;
147
148     dstctx = OPENSSL_zalloc(sizeof(*srcctx));
149
150     *dstctx = *srcctx;
151     if (dstctx->dh != NULL && !DH_up_ref(dstctx->dh)) {
152         OPENSSL_free(dstctx);
153         return NULL;
154     }
155
156     if (dstctx->dhpeer != NULL && !DH_up_ref(dstctx->dhpeer)) {
157         DH_free(dstctx->dh);
158         OPENSSL_free(dstctx);
159         return NULL;
160     }
161
162     return dstctx;
163 }
164
165 const OSSL_DISPATCH dh_functions[] = {
166     { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))dh_newctx },
167     { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))dh_init },
168     { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))dh_derive },
169     { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))dh_set_peer },
170     { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))dh_freectx },
171     { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))dh_dupctx },
172     { 0, NULL }
173 };