forked from brunorijsman/openssl-qkd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathqkd_engine_server.c
179 lines (156 loc) · 6.55 KB
/
qkd_engine_server.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/**
* qkd_engine_server.c
*
* An OpenSSL Engine for OpenSSL servers that "hijacks" the existing Diffie-Helman key agreement
* protocol in OpenSSL to implement a QKD key agreement on top of the ETSI QKD API.
* There is a separate engine for OpenSSL clients (see qkd_engine_client.c).
* See qkd_api.h for the definition of the ETSI QKD API.
*
* (c) 2019 Bruno Rijsman, All Rights Reserved.
* See LICENSE for licensing information.
*/
#include "qkd_engine_common.h"
#include "qkd_debug.h"
#include <assert.h>
#include <string.h>
#include <openssl/engine.h>
/**
* Callback which registered in the client OpenSSL engine to be called when OpenSSL needs the engine
* to generate a Diffie-Hellman private key and to derive the Diffie-Hellman public key from it.
*
* Returns 1 on success, 0 on failure.
*/
static int server_generate_key(DH *dh)
{
QKD_enter();
/* Generate the private key. Always use a fixed private key (it is not actually used for
* anything.) */
BIGNUM *private_key = BN_secure_new();
if (NULL == private_key) {
QKD_error("BN_secure_new (private_key) failed");
QKD_return_error("%d", 0);
}
BN_set_word(private_key, QKD_fixed_private_key);
QKD_debug("DH private key: %s", BN_bn2hex(private_key));
/* Generate the public key. */
BIGNUM *public_key = BN_secure_new();
if (public_key == NULL) {
QKD_error("BN_secure_new (public_key) failed");
QKD_return_error("%d", 0);
}
if (QKD_return_fixed_key_for_testing) {
QKD_debug("Use fixed public key (for testing)");
BN_set_word(public_key, QKD_fixed_public_key);
} else {
QKD_debug("Encode the ETSI QKD API key handle into the public key");
/* TODO: Also encode the local address of the listening socket into the public key */
/* Use fixed QoS parameters. */
int shared_secret_size = DH_size(dh);
QKD_qos_t qos = {
.requested_length = shared_secret_size,
.max_bps = 0,
.priority = 0,
.timeout = 0
};
/* Call QKD_open with a null key handle. This will cause a new key handle to be allocated.
* Set destination to NULL, which means we don't care who the remote peer is (we rely on
* SSL authentication). */
QKD_key_handle_t key_handle = QKD_key_handle_null;
QKD_result_t qkd_result = QKD_open(NULL, qos, &key_handle);
if (QKD_RESULT_SUCCESS != qkd_result) {
QKD_error("QKD_open failed: %s", QKD_result_str(qkd_result));
QKD_return_error_qkd(qkd_result);
}
QKD_debug("Allocated key handle: %s", QKD_key_handle_str(&key_handle));
/* Convert allocated key handle to bignum and use it as the public key */
QKD_key_handle_to_bignum(&key_handle, public_key);
}
QKD_debug("DH public key: %s", BN_bn2hex(public_key));
int result = DH_set0_key(dh, public_key, private_key);
if(1 != result) {
QKD_error("DH_set0_key failed");
QKD_return_error_qkd(QKD_STATUS_OPEN_SSL_ERROR);
}
QKD_return_success("%d", 1);
}
/**
* Callback which registered in the client OpenSSL engine to be called when OpenSSL needs the engine
* to compute the Diffie-Hellman shared secret based on Diffie-Hellman parameters, the server public
* key, and our own (the client's) private key.
*
* Returns the size of the generated shared secret on success, -1 on failure.
*/
static int server_compute_key(unsigned char *shared_secret, const BIGNUM *client_public_key, DH *dh)
{
QKD_enter();
/* Retrieve the key handle, which is stored in our own (i.e. the server's) public key. */
const BIGNUM *server_public_key = NULL;
DH_get0_key(dh, &server_public_key, NULL);
assert(server_public_key != NULL);
QKD_key_handle_t key_handle = QKD_key_handle_null;
int convert_result = QKD_bignum_to_key_handle(server_public_key, &key_handle);
if (convert_result != 1) {
QKD_error("QKD_bignum_to_key_handle failed (return code %d)", convert_result);
QKD_return_error("%d", -1);
}
QKD_debug("Key handle = %s", QKD_key_handle_str(&key_handle));
/* Connect to the QKD peer. We cannot do this earlier (specifically we cannot not do this in
* server_generate_key) because the connection can only be completed *after* the client has
* already received the TLS Server Hello message, which contains the key handle in the
* Diffie-Hellman public key. The client needs the key handle to be able to complete the
* connection. */
/* TODO: right value for timeout? */
QKD_result_t qkd_result = QKD_connect_blocking(&key_handle, 0);
if (QKD_RESULT_SUCCESS != qkd_result) {
QKD_error("QKD_connect_blocking failed (return code %d)", convert_result);
QKD_return_error("%d", -1);
}
/* Get the shared key from the QKD provider. Note that the ETSI API wants the key to be signed
* chars, but OpenSSL wants it to be unsigned chars. */
qkd_result = QKD_get_key(&key_handle, (char *) shared_secret);
if (QKD_RESULT_SUCCESS != qkd_result) {
QKD_error("QKD_get_key failed (return code %d)", convert_result);
QKD_return_error("%d", -1);
}
int shared_secret_size = DH_size(dh);
QKD_debug("shared secret = %s", QKD_shared_secret_str((char *)shared_secret,
shared_secret_size));
/* Close the QKD session. */
qkd_result = QKD_close(&key_handle);
if (QKD_RESULT_SUCCESS != qkd_result) {
QKD_error("QKD_close failed (return code %d)", convert_result);
QKD_return_error("%d", -1);
}
QKD_return_success("%d", shared_secret_size);
}
/**
* Initialize the engine.
*
* Returns 1 on success, 0 on failure.
*/
static int server_engine_init(ENGINE *engine)
{
QKD_enter();
QKD_result_t qkd_result = QKD_init(true);
if (QKD_RESULT_SUCCESS != qkd_result) {
QKD_error("QKD_init failed: ", QKD_result_str(qkd_result));
QKD_return_error("%d", -1);
}
QKD_return_success("%d", 1);
}
/**
* Bind this client engine to OpenSSL.
*
* Returns 1 on success, 0 on failure.
*/int server_engine_bind(ENGINE *engine, const char *engine_id)
{
QKD_enter();
int result = QKD_engine_bind(engine, "qkd_engine_server", "QKD Server Engine", server_generate_key,
server_compute_key, server_engine_init);
if (1 != result) {
QKD_error("QKD_engine_bind failed (return code %d)", result);
QKD_return_error("%d", 0);
}
QKD_return_success("%d", 1);}
IMPLEMENT_DYNAMIC_CHECK_FN();
IMPLEMENT_DYNAMIC_BIND_FN(server_engine_bind);