blob: 9953f82a044b32f6b5962887fdb0c601460188ca [file] [log] [blame]
/*
* Copyright 2006-2019 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <openssl/evp.h>
#include <assert.h>
#include <openssl/dh.h>
#include <openssl/err.h>
#include <openssl/mem.h>
#include "internal.h"
typedef struct dh_pkey_ctx_st {
int pad;
} DH_PKEY_CTX;
static int pkey_dh_init(EVP_PKEY_CTX *ctx) {
DH_PKEY_CTX *dctx = OPENSSL_zalloc(sizeof(DH_PKEY_CTX));
if (dctx == NULL) {
return 0;
}
ctx->data = dctx;
return 1;
}
static int pkey_dh_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) {
if (!pkey_dh_init(dst)) {
return 0;
}
const DH_PKEY_CTX *sctx = src->data;
DH_PKEY_CTX *dctx = dst->data;
dctx->pad = sctx->pad;
return 1;
}
static void pkey_dh_cleanup(EVP_PKEY_CTX *ctx) {
OPENSSL_free(ctx->data);
ctx->data = NULL;
}
static int pkey_dh_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
DH *dh = DH_new();
if (dh == NULL || !EVP_PKEY_assign_DH(pkey, dh)) {
DH_free(dh);
return 0;
}
if (ctx->pkey != NULL && !EVP_PKEY_copy_parameters(pkey, ctx->pkey)) {
return 0;
}
return DH_generate_key(dh);
}
static int pkey_dh_derive(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *out_len) {
DH_PKEY_CTX *dctx = ctx->data;
if (ctx->pkey == NULL || ctx->peerkey == NULL) {
OPENSSL_PUT_ERROR(EVP, EVP_R_KEYS_NOT_SET);
return 0;
}
DH *our_key = ctx->pkey->pkey;
DH *peer_key = ctx->peerkey->pkey;
if (our_key == NULL || peer_key == NULL) {
OPENSSL_PUT_ERROR(EVP, EVP_R_KEYS_NOT_SET);
return 0;
}
const BIGNUM *pub_key = DH_get0_pub_key(peer_key);
if (pub_key == NULL) {
OPENSSL_PUT_ERROR(EVP, EVP_R_KEYS_NOT_SET);
return 0;
}
if (out == NULL) {
*out_len = DH_size(our_key);
return 1;
}
if (*out_len < (size_t)DH_size(our_key)) {
OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
return 0;
}
int ret = dctx->pad ? DH_compute_key_padded(out, pub_key, our_key)
: DH_compute_key(out, pub_key, our_key);
if (ret < 0) {
return 0;
}
assert(ret <= DH_size(our_key));
*out_len = (size_t)ret;
return 1;
}
static int pkey_dh_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) {
DH_PKEY_CTX *dctx = ctx->data;
switch (type) {
case EVP_PKEY_CTRL_PEER_KEY:
// |EVP_PKEY_derive_set_peer| requires the key implement this command,
// even if it is a no-op.
return 1;
case EVP_PKEY_CTRL_DH_PAD:
dctx->pad = p1;
return 1;
default:
OPENSSL_PUT_ERROR(EVP, EVP_R_COMMAND_NOT_SUPPORTED);
return 0;
}
}
const EVP_PKEY_METHOD dh_pkey_meth = {
.pkey_id = EVP_PKEY_DH,
.init = pkey_dh_init,
.copy = pkey_dh_copy,
.cleanup = pkey_dh_cleanup,
.keygen = pkey_dh_keygen,
.derive = pkey_dh_derive,
.ctrl = pkey_dh_ctrl,
};
int EVP_PKEY_CTX_set_dh_pad(EVP_PKEY_CTX *ctx, int pad) {
return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_DERIVE,
EVP_PKEY_CTRL_DH_PAD, pad, NULL);
}