1 /*
2 * Copyright 2006-2019 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the OpenSSL license (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/evp.h>
11
12 #include <assert.h>
13
14 #include <openssl/dh.h>
15 #include <openssl/err.h>
16 #include <openssl/mem.h>
17
18 #include "internal.h"
19
20
21 typedef struct dh_pkey_ctx_st {
22 int pad;
23 } DH_PKEY_CTX;
24
pkey_dh_init(EVP_PKEY_CTX * ctx)25 static int pkey_dh_init(EVP_PKEY_CTX *ctx) {
26 DH_PKEY_CTX *dctx = OPENSSL_zalloc(sizeof(DH_PKEY_CTX));
27 if (dctx == NULL) {
28 return 0;
29 }
30
31 ctx->data = dctx;
32 return 1;
33 }
34
pkey_dh_copy(EVP_PKEY_CTX * dst,EVP_PKEY_CTX * src)35 static int pkey_dh_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) {
36 if (!pkey_dh_init(dst)) {
37 return 0;
38 }
39
40 const DH_PKEY_CTX *sctx = src->data;
41 DH_PKEY_CTX *dctx = dst->data;
42 dctx->pad = sctx->pad;
43 return 1;
44 }
45
pkey_dh_cleanup(EVP_PKEY_CTX * ctx)46 static void pkey_dh_cleanup(EVP_PKEY_CTX *ctx) {
47 OPENSSL_free(ctx->data);
48 ctx->data = NULL;
49 }
50
pkey_dh_keygen(EVP_PKEY_CTX * ctx,EVP_PKEY * pkey)51 static int pkey_dh_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
52 DH *dh = DH_new();
53 if (dh == NULL || !EVP_PKEY_assign_DH(pkey, dh)) {
54 DH_free(dh);
55 return 0;
56 }
57
58 if (ctx->pkey != NULL && !EVP_PKEY_copy_parameters(pkey, ctx->pkey)) {
59 return 0;
60 }
61
62 return DH_generate_key(dh);
63 }
64
pkey_dh_derive(EVP_PKEY_CTX * ctx,uint8_t * out,size_t * out_len)65 static int pkey_dh_derive(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *out_len) {
66 DH_PKEY_CTX *dctx = ctx->data;
67 if (ctx->pkey == NULL || ctx->peerkey == NULL) {
68 OPENSSL_PUT_ERROR(EVP, EVP_R_KEYS_NOT_SET);
69 return 0;
70 }
71
72 DH *our_key = ctx->pkey->pkey;
73 DH *peer_key = ctx->peerkey->pkey;
74 if (our_key == NULL || peer_key == NULL) {
75 OPENSSL_PUT_ERROR(EVP, EVP_R_KEYS_NOT_SET);
76 return 0;
77 }
78
79 const BIGNUM *pub_key = DH_get0_pub_key(peer_key);
80 if (pub_key == NULL) {
81 OPENSSL_PUT_ERROR(EVP, EVP_R_KEYS_NOT_SET);
82 return 0;
83 }
84
85 if (out == NULL) {
86 *out_len = DH_size(our_key);
87 return 1;
88 }
89
90 if (*out_len < (size_t)DH_size(our_key)) {
91 OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
92 return 0;
93 }
94
95 int ret = dctx->pad ? DH_compute_key_padded(out, pub_key, our_key)
96 : DH_compute_key(out, pub_key, our_key);
97 if (ret < 0) {
98 return 0;
99 }
100
101 assert(ret <= DH_size(our_key));
102 *out_len = (size_t)ret;
103 return 1;
104 }
105
pkey_dh_ctrl(EVP_PKEY_CTX * ctx,int type,int p1,void * p2)106 static int pkey_dh_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) {
107 DH_PKEY_CTX *dctx = ctx->data;
108 switch (type) {
109 case EVP_PKEY_CTRL_PEER_KEY:
110 // |EVP_PKEY_derive_set_peer| requires the key implement this command,
111 // even if it is a no-op.
112 return 1;
113
114 case EVP_PKEY_CTRL_DH_PAD:
115 dctx->pad = p1;
116 return 1;
117
118 default:
119 OPENSSL_PUT_ERROR(EVP, EVP_R_COMMAND_NOT_SUPPORTED);
120 return 0;
121 }
122 }
123
124 const EVP_PKEY_METHOD dh_pkey_meth = {
125 .pkey_id = EVP_PKEY_DH,
126 .init = pkey_dh_init,
127 .copy = pkey_dh_copy,
128 .cleanup = pkey_dh_cleanup,
129 .keygen = pkey_dh_keygen,
130 .derive = pkey_dh_derive,
131 .ctrl = pkey_dh_ctrl,
132 };
133
EVP_PKEY_CTX_set_dh_pad(EVP_PKEY_CTX * ctx,int pad)134 int EVP_PKEY_CTX_set_dh_pad(EVP_PKEY_CTX *ctx, int pad) {
135 return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_DERIVE,
136 EVP_PKEY_CTRL_DH_PAD, pad, NULL);
137 }
138