1 1.1 christos /* 2 1.1 christos * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. 3 1.1 christos * 4 1.1 christos * Licensed under the Apache License 2.0 (the "License"). You may not use 5 1.1 christos * this file except in compliance with the License. You can obtain a copy 6 1.1 christos * in the file LICENSE in the source distribution or at 7 1.1 christos * https://www.openssl.org/source/license.html 8 1.1 christos */ 9 1.1 christos 10 1.1 christos #include <stdio.h> 11 1.1 christos #include <string.h> 12 1.1 christos #include <openssl/core_names.h> 13 1.1 christos #include <openssl/evp.h> 14 1.1 christos 15 1.1 christos /* 16 1.1 christos * This is a demonstration of key exchange using X25519. 17 1.1 christos * 18 1.1 christos * The variables beginning `peer1_` / `peer2_` are data which would normally be 19 1.1 christos * accessible to that peer. 20 1.1 christos * 21 1.1 christos * Ordinarily you would use random keys, which are demonstrated 22 1.1 christos * below when use_kat=0. A known answer test is demonstrated 23 1.1 christos * when use_kat=1. 24 1.1 christos */ 25 1.1 christos 26 1.1 christos /* A property query used for selecting the X25519 implementation. */ 27 1.1 christos static const char *propq = NULL; 28 1.1 christos 29 1.1 christos static const unsigned char peer1_privk_data[32] = { 30 1.1 christos 0x80, 0x5b, 0x30, 0x20, 0x25, 0x4a, 0x70, 0x2c, 31 1.1 christos 0xad, 0xa9, 0x8d, 0x7d, 0x47, 0xf8, 0x1b, 0x20, 32 1.1 christos 0x89, 0xd2, 0xf9, 0x14, 0xac, 0x92, 0x27, 0xf2, 33 1.1 christos 0x10, 0x7e, 0xdb, 0x21, 0xbd, 0x73, 0x73, 0x5d 34 1.1 christos }; 35 1.1 christos 36 1.1 christos static const unsigned char peer2_privk_data[32] = { 37 1.1 christos 0xf8, 0x84, 0x19, 0x69, 0x79, 0x13, 0x0d, 0xbd, 38 1.1 christos 0xb1, 0x76, 0xd7, 0x0e, 0x7e, 0x0f, 0xb6, 0xf4, 39 1.1 christos 0x8c, 0x4a, 0x8c, 0x5f, 0xd8, 0x15, 0x09, 0x0a, 40 1.1 christos 0x71, 0x78, 0x74, 0x92, 0x0f, 0x85, 0xc8, 0x43 41 1.1 christos }; 42 1.1 christos 43 1.1 christos static const unsigned char expected_result[32] = { 44 1.1 christos 0x19, 0x71, 0x26, 0x12, 0x74, 0xb5, 0xb1, 0xce, 45 1.1 christos 0x77, 0xd0, 0x79, 0x24, 0xb6, 0x0a, 0x5c, 0x72, 46 1.1 christos 0x0c, 0xa6, 0x56, 0xc0, 0x11, 0xeb, 0x43, 0x11, 47 1.1 christos 0x94, 0x3b, 0x01, 0x45, 0xca, 0x19, 0xfe, 0x09 48 1.1 christos }; 49 1.1 christos 50 1.1 christos typedef struct peer_data_st { 51 1.1 christos const char *name; /* name of peer */ 52 1.1 christos EVP_PKEY *privk; /* privk generated for peer */ 53 1.1 christos unsigned char pubk_data[32]; /* generated pubk to send to other peer */ 54 1.1 christos 55 1.1 christos unsigned char *secret; /* allocated shared secret buffer */ 56 1.1 christos size_t secret_len; 57 1.1 christos } PEER_DATA; 58 1.1 christos 59 1.1 christos /* 60 1.1 christos * Prepare for X25519 key exchange. The public key to be sent to the remote peer 61 1.1 christos * is put in pubk_data, which should be a 32-byte buffer. Returns 1 on success. 62 1.1 christos */ 63 1.1 christos static int keyexch_x25519_before( 64 1.1 christos OSSL_LIB_CTX *libctx, 65 1.1 christos const unsigned char *kat_privk_data, 66 1.1 christos PEER_DATA *local_peer) 67 1.1 christos { 68 1.1 christos int rv = 0; 69 1.1 christos size_t pubk_data_len = 0; 70 1.1 christos 71 1.1 christos /* Generate or load X25519 key for the peer */ 72 1.1 christos if (kat_privk_data != NULL) 73 1.1 christos local_peer->privk = 74 1.1 christos EVP_PKEY_new_raw_private_key_ex(libctx, "X25519", propq, 75 1.1 christos kat_privk_data, 76 1.1 christos sizeof(peer1_privk_data)); 77 1.1 christos else 78 1.1 christos local_peer->privk = EVP_PKEY_Q_keygen(libctx, propq, "X25519"); 79 1.1 christos 80 1.1 christos if (local_peer->privk == NULL) { 81 1.1 christos fprintf(stderr, "Could not load or generate private key\n"); 82 1.1 christos goto end; 83 1.1 christos } 84 1.1 christos 85 1.1 christos /* Get public key corresponding to the private key */ 86 1.1 christos if (EVP_PKEY_get_octet_string_param(local_peer->privk, 87 1.1 christos OSSL_PKEY_PARAM_PUB_KEY, 88 1.1 christos local_peer->pubk_data, 89 1.1 christos sizeof(local_peer->pubk_data), 90 1.1 christos &pubk_data_len) == 0) { 91 1.1 christos fprintf(stderr, "EVP_PKEY_get_octet_string_param() failed\n"); 92 1.1 christos goto end; 93 1.1 christos } 94 1.1 christos 95 1.1 christos /* X25519 public keys are always 32 bytes */ 96 1.1 christos if (pubk_data_len != 32) { 97 1.1 christos fprintf(stderr, "EVP_PKEY_get_octet_string_param() " 98 1.1 christos "yielded wrong length\n"); 99 1.1 christos goto end; 100 1.1 christos } 101 1.1 christos 102 1.1 christos rv = 1; 103 1.1 christos end: 104 1.1 christos if (rv == 0) { 105 1.1 christos EVP_PKEY_free(local_peer->privk); 106 1.1 christos local_peer->privk = NULL; 107 1.1 christos } 108 1.1 christos 109 1.1 christos return rv; 110 1.1 christos } 111 1.1 christos 112 1.1 christos /* 113 1.1 christos * Complete X25519 key exchange. remote_peer_pubk_data should be the 32 byte 114 1.1 christos * public key value received from the remote peer. On success, returns 1 and the 115 1.1 christos * secret is pointed to by *secret. The caller must free it. 116 1.1 christos */ 117 1.1 christos static int keyexch_x25519_after( 118 1.1 christos OSSL_LIB_CTX *libctx, 119 1.1 christos int use_kat, 120 1.1 christos PEER_DATA *local_peer, 121 1.1 christos const unsigned char *remote_peer_pubk_data) 122 1.1 christos { 123 1.1 christos int rv = 0; 124 1.1 christos EVP_PKEY *remote_peer_pubk = NULL; 125 1.1 christos EVP_PKEY_CTX *ctx = NULL; 126 1.1 christos 127 1.1 christos local_peer->secret = NULL; 128 1.1 christos 129 1.1 christos /* Load public key for remote peer. */ 130 1.1 christos remote_peer_pubk = 131 1.1 christos EVP_PKEY_new_raw_public_key_ex(libctx, "X25519", propq, 132 1.1 christos remote_peer_pubk_data, 32); 133 1.1 christos if (remote_peer_pubk == NULL) { 134 1.1 christos fprintf(stderr, "EVP_PKEY_new_raw_public_key_ex() failed\n"); 135 1.1 christos goto end; 136 1.1 christos } 137 1.1 christos 138 1.1 christos /* Create key exchange context. */ 139 1.1 christos ctx = EVP_PKEY_CTX_new_from_pkey(libctx, local_peer->privk, propq); 140 1.1 christos if (ctx == NULL) { 141 1.1 christos fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey() failed\n"); 142 1.1 christos goto end; 143 1.1 christos } 144 1.1 christos 145 1.1 christos /* Initialize derivation process. */ 146 1.1 christos if (EVP_PKEY_derive_init(ctx) == 0) { 147 1.1 christos fprintf(stderr, "EVP_PKEY_derive_init() failed\n"); 148 1.1 christos goto end; 149 1.1 christos } 150 1.1 christos 151 1.1 christos /* Configure each peer with the other peer's public key. */ 152 1.1 christos if (EVP_PKEY_derive_set_peer(ctx, remote_peer_pubk) == 0) { 153 1.1 christos fprintf(stderr, "EVP_PKEY_derive_set_peer() failed\n"); 154 1.1 christos goto end; 155 1.1 christos } 156 1.1 christos 157 1.1 christos /* Determine the secret length. */ 158 1.1 christos if (EVP_PKEY_derive(ctx, NULL, &local_peer->secret_len) == 0) { 159 1.1 christos fprintf(stderr, "EVP_PKEY_derive() failed\n"); 160 1.1 christos goto end; 161 1.1 christos } 162 1.1 christos 163 1.1 christos /* 164 1.1 christos * We are using X25519, so the secret generated will always be 32 bytes. 165 1.1 christos * However for exposition, the code below demonstrates a generic 166 1.1 christos * implementation for arbitrary lengths. 167 1.1 christos */ 168 1.1 christos if (local_peer->secret_len != 32) { /* unreachable */ 169 1.1 christos fprintf(stderr, "Secret is always 32 bytes for X25519\n"); 170 1.1 christos goto end; 171 1.1 christos } 172 1.1 christos 173 1.1 christos /* Allocate memory for shared secrets. */ 174 1.1 christos local_peer->secret = OPENSSL_malloc(local_peer->secret_len); 175 1.1 christos if (local_peer->secret == NULL) { 176 1.1 christos fprintf(stderr, "Could not allocate memory for secret\n"); 177 1.1 christos goto end; 178 1.1 christos } 179 1.1 christos 180 1.1 christos /* Derive the shared secret. */ 181 1.1 christos if (EVP_PKEY_derive(ctx, local_peer->secret, 182 1.1 christos &local_peer->secret_len) == 0) { 183 1.1 christos fprintf(stderr, "EVP_PKEY_derive() failed\n"); 184 1.1 christos goto end; 185 1.1 christos } 186 1.1 christos 187 1.1 christos printf("Shared secret (%s):\n", local_peer->name); 188 1.1 christos BIO_dump_indent_fp(stdout, local_peer->secret, local_peer->secret_len, 2); 189 1.1 christos putchar('\n'); 190 1.1 christos 191 1.1 christos rv = 1; 192 1.1 christos end: 193 1.1 christos EVP_PKEY_CTX_free(ctx); 194 1.1 christos EVP_PKEY_free(remote_peer_pubk); 195 1.1 christos if (rv == 0) { 196 1.1 christos OPENSSL_clear_free(local_peer->secret, local_peer->secret_len); 197 1.1 christos local_peer->secret = NULL; 198 1.1 christos } 199 1.1 christos 200 1.1 christos return rv; 201 1.1 christos } 202 1.1 christos 203 1.1 christos static int keyexch_x25519(int use_kat) 204 1.1 christos { 205 1.1 christos int rv = 0; 206 1.1 christos OSSL_LIB_CTX *libctx = NULL; 207 1.1 christos PEER_DATA peer1 = {"peer 1"}, peer2 = {"peer 2"}; 208 1.1 christos 209 1.1 christos /* 210 1.1 christos * Each peer generates its private key and sends its public key 211 1.1 christos * to the other peer. The private key is stored locally for 212 1.1 christos * later use. 213 1.1 christos */ 214 1.1 christos if (keyexch_x25519_before(libctx, use_kat ? peer1_privk_data : NULL, 215 1.1 christos &peer1) == 0) 216 1.1 christos return 0; 217 1.1 christos 218 1.1 christos if (keyexch_x25519_before(libctx, use_kat ? peer2_privk_data : NULL, 219 1.1 christos &peer2) == 0) 220 1.1 christos return 0; 221 1.1 christos 222 1.1 christos /* 223 1.1 christos * Each peer uses the other peer's public key to perform key exchange. 224 1.1 christos * After this succeeds, each peer has the same secret in its 225 1.1 christos * PEER_DATA. 226 1.1 christos */ 227 1.1 christos if (keyexch_x25519_after(libctx, use_kat, &peer1, peer2.pubk_data) == 0) 228 1.1 christos return 0; 229 1.1 christos 230 1.1 christos if (keyexch_x25519_after(libctx, use_kat, &peer2, peer1.pubk_data) == 0) 231 1.1 christos return 0; 232 1.1 christos 233 1.1 christos /* 234 1.1 christos * Here we demonstrate the secrets are equal for exposition purposes. 235 1.1 christos * 236 1.1 christos * Although in practice you will generally not need to compare secrets 237 1.1 christos * produced through key exchange, if you do compare cryptographic secrets, 238 1.1 christos * always do so using a constant-time function such as CRYPTO_memcmp, never 239 1.1 christos * using memcmp(3). 240 1.1 christos */ 241 1.1 christos if (CRYPTO_memcmp(peer1.secret, peer2.secret, peer1.secret_len) != 0) { 242 1.1 christos fprintf(stderr, "Negotiated secrets do not match\n"); 243 1.1 christos goto end; 244 1.1 christos } 245 1.1 christos 246 1.1 christos /* If we are doing the KAT, the secret should equal our reference result. */ 247 1.1 christos if (use_kat && CRYPTO_memcmp(peer1.secret, expected_result, 248 1.1 christos peer1.secret_len) != 0) { 249 1.1 christos fprintf(stderr, "Did not get expected result\n"); 250 1.1 christos goto end; 251 1.1 christos } 252 1.1 christos 253 1.1 christos rv = 1; 254 1.1 christos end: 255 1.1 christos /* The secrets are sensitive, so ensure they are erased before freeing. */ 256 1.1 christos OPENSSL_clear_free(peer1.secret, peer1.secret_len); 257 1.1 christos OPENSSL_clear_free(peer2.secret, peer2.secret_len); 258 1.1 christos 259 1.1 christos EVP_PKEY_free(peer1.privk); 260 1.1 christos EVP_PKEY_free(peer2.privk); 261 1.1 christos OSSL_LIB_CTX_free(libctx); 262 1.1 christos return rv; 263 1.1 christos } 264 1.1 christos 265 1.1 christos int main(int argc, char **argv) 266 1.1 christos { 267 1.1 christos /* Test X25519 key exchange with known result. */ 268 1.1 christos printf("Key exchange using known answer (deterministic):\n"); 269 1.1 christos if (keyexch_x25519(1) == 0) 270 1.1 christos return 1; 271 1.1 christos 272 1.1 christos /* Test X25519 key exchange with random keys. */ 273 1.1 christos printf("Key exchange using random keys:\n"); 274 1.1 christos if (keyexch_x25519(0) == 0) 275 1.1 christos return 1; 276 1.1 christos 277 1.1 christos return 0; 278 1.1 christos } 279