Skip to content

Commit 2c65707

Browse files
davidbenagl
authored andcommitted
Add EVP_PKEY support for X25519.
cryptography.io expects X25519 support to be exposed via EVP_PKEY. Also we're considering using EVP_PKEY to pass in keys for ESNI. This unfortunately requires adding some odd EVP_PKEY_set1_tls_encodedpoint and EVP_PKEY_get1_tls_encodedpoint APIs which cryptography.io uses for X25519 because the EVP_PKEY "raw" functions did not exist at the time. To test, implement EVP_PKEY_derive support in evp_tests.txt. Change-Id: Ie0666bb9aba13eecf203156dc047ac49ef6d0093 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/36788 Reviewed-by: Adam Langley <[email protected]>
1 parent a866ba5 commit 2c65707

File tree

11 files changed

+522
-19
lines changed

11 files changed

+522
-19
lines changed

crypto/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,8 @@ add_library(
282282
evp/p_ed25519_asn1.c
283283
evp/p_rsa.c
284284
evp/p_rsa_asn1.c
285+
evp/p_x25519.c
286+
evp/p_x25519_asn1.c
285287
evp/pbkdf.c
286288
evp/print.c
287289
evp/scrypt.c

crypto/err/evp.errordata

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ EVP,113,INVALID_MGF1_MD
1515
EVP,114,INVALID_OPERATION
1616
EVP,115,INVALID_PADDING_MODE
1717
EVP,133,INVALID_PARAMETERS
18+
EVP,134,INVALID_PEER_KEY
1819
EVP,116,INVALID_PSS_SALTLEN
1920
EVP,131,INVALID_SIGNATURE
2021
EVP,117,KEYS_NOT_SET

crypto/evp/evp.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ static const EVP_PKEY_ASN1_METHOD *evp_pkey_asn1_find(int nid) {
200200
return &dsa_asn1_meth;
201201
case EVP_PKEY_ED25519:
202202
return &ed25519_asn1_meth;
203+
case EVP_PKEY_X25519:
204+
return &x25519_asn1_meth;
203205
default:
204206
return NULL;
205207
}

crypto/evp/evp_asn1.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ static const EVP_PKEY_ASN1_METHOD *const kASN1Methods[] = {
7373
&ec_asn1_meth,
7474
&dsa_asn1_meth,
7575
&ed25519_asn1_meth,
76+
&x25519_asn1_meth,
7677
};
7778

7879
static int parse_key_type(CBS *cbs, int *out_type) {

crypto/evp/evp_ctx.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,14 @@
6767

6868

6969
static const EVP_PKEY_METHOD *const evp_methods[] = {
70-
&rsa_pkey_meth,
71-
&ec_pkey_meth,
72-
&ed25519_pkey_meth,
70+
&rsa_pkey_meth,
71+
&ec_pkey_meth,
72+
&ed25519_pkey_meth,
73+
&x25519_pkey_meth,
7374
};
7475

7576
static const EVP_PKEY_METHOD *evp_pkey_meth_find(int type) {
76-
unsigned i;
77-
78-
for (i = 0; i < sizeof(evp_methods)/sizeof(EVP_PKEY_METHOD*); i++) {
77+
for (size_t i = 0; i < sizeof(evp_methods)/sizeof(EVP_PKEY_METHOD*); i++) {
7978
if (evp_methods[i]->pkey_id == type) {
8079
return evp_methods[i];
8180
}

crypto/evp/evp_test.cc

Lines changed: 86 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ static int GetKeyType(FileTest *t, const std::string &name) {
119119
if (name == "Ed25519") {
120120
return EVP_PKEY_ED25519;
121121
}
122+
if (name == "X25519") {
123+
return EVP_PKEY_X25519;
124+
}
122125
ADD_FAILURE() << "Unknown key type: " << name;
123126
return EVP_PKEY_NONE;
124127
}
@@ -245,7 +248,7 @@ static bool ImportKey(FileTest *t, KeyMap *key_map,
245248

246249
// SetupContext configures |ctx| based on attributes in |t|, with the exception
247250
// of the signing digest which must be configured externally.
248-
static bool SetupContext(FileTest *t, EVP_PKEY_CTX *ctx) {
251+
static bool SetupContext(FileTest *t, KeyMap *key_map, EVP_PKEY_CTX *ctx) {
249252
if (t->HasAttribute("RSAPadding")) {
250253
int padding;
251254
if (!GetRSAPadding(t, &padding, t->GetAttributeOrDie("RSAPadding")) ||
@@ -285,6 +288,74 @@ static bool SetupContext(FileTest *t, EVP_PKEY_CTX *ctx) {
285288
}
286289
buf.release();
287290
}
291+
if (t->HasAttribute("DerivePeer")) {
292+
std::string derive_peer = t->GetAttributeOrDie("DerivePeer");
293+
if (key_map->count(derive_peer) == 0) {
294+
ADD_FAILURE() << "Could not find key " << derive_peer;
295+
return false;
296+
}
297+
EVP_PKEY *derive_peer_key = (*key_map)[derive_peer].get();
298+
if (!EVP_PKEY_derive_set_peer(ctx, derive_peer_key)) {
299+
return false;
300+
}
301+
}
302+
return true;
303+
}
304+
305+
static bool TestDerive(FileTest *t, KeyMap *key_map, EVP_PKEY *key) {
306+
bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new(key, nullptr));
307+
if (!ctx ||
308+
!EVP_PKEY_derive_init(ctx.get()) ||
309+
!SetupContext(t, key_map, ctx.get())) {
310+
return false;
311+
}
312+
313+
bssl::UniquePtr<EVP_PKEY_CTX> copy(EVP_PKEY_CTX_dup(ctx.get()));
314+
if (!copy) {
315+
return false;
316+
}
317+
318+
for (EVP_PKEY_CTX *pctx : {ctx.get(), copy.get()}) {
319+
size_t len;
320+
std::vector<uint8_t> actual, output;
321+
if (!EVP_PKEY_derive(pctx, nullptr, &len)) {
322+
return false;
323+
}
324+
actual.resize(len);
325+
if (!EVP_PKEY_derive(pctx, actual.data(), &len)) {
326+
return false;
327+
}
328+
actual.resize(len);
329+
330+
// Defer looking up the attribute so Error works properly.
331+
if (!t->GetBytes(&output, "Output")) {
332+
return false;
333+
}
334+
EXPECT_EQ(Bytes(output), Bytes(actual));
335+
336+
// Test when the buffer is too large.
337+
actual.resize(len + 1);
338+
len = actual.size();
339+
if (!EVP_PKEY_derive(pctx, actual.data(), &len)) {
340+
return false;
341+
}
342+
actual.resize(len);
343+
EXPECT_EQ(Bytes(output), Bytes(actual));
344+
345+
// Test when the buffer is too small.
346+
actual.resize(len - 1);
347+
len = actual.size();
348+
if (t->HasAttribute("SmallBufferTruncates")) {
349+
if (!EVP_PKEY_derive(pctx, actual.data(), &len)) {
350+
return false;
351+
}
352+
actual.resize(len);
353+
EXPECT_EQ(Bytes(output.data(), len), Bytes(actual));
354+
} else {
355+
EXPECT_FALSE(EVP_PKEY_derive(pctx, actual.data(), &len));
356+
ERR_clear_error();
357+
}
358+
}
288359
return true;
289360
}
290361

@@ -298,6 +369,14 @@ static bool TestEVP(FileTest *t, KeyMap *key_map) {
298369
return ImportKey(t, key_map, EVP_parse_public_key, EVP_marshal_public_key);
299370
}
300371

372+
// Load the key.
373+
const std::string &key_name = t->GetParameter();
374+
if (key_map->count(key_name) == 0) {
375+
ADD_FAILURE() << "Could not find key " << key_name;
376+
return false;
377+
}
378+
EVP_PKEY *key = (*key_map)[key_name].get();
379+
301380
int (*key_op_init)(EVP_PKEY_CTX *ctx) = nullptr;
302381
int (*key_op)(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *out_len,
303382
const uint8_t *in, size_t in_len) = nullptr;
@@ -321,19 +400,13 @@ static bool TestEVP(FileTest *t, KeyMap *key_map) {
321400
} else if (t->GetType() == "Encrypt") {
322401
key_op_init = EVP_PKEY_encrypt_init;
323402
key_op = EVP_PKEY_encrypt;
403+
} else if (t->GetType() == "Derive") {
404+
return TestDerive(t, key_map, key);
324405
} else {
325406
ADD_FAILURE() << "Unknown test " << t->GetType();
326407
return false;
327408
}
328409

329-
// Load the key.
330-
const std::string &key_name = t->GetParameter();
331-
if (key_map->count(key_name) == 0) {
332-
ADD_FAILURE() << "Could not find key " << key_name;
333-
return false;
334-
}
335-
EVP_PKEY *key = (*key_map)[key_name].get();
336-
337410
const EVP_MD *digest = nullptr;
338411
if (t->HasAttribute("Digest")) {
339412
digest = GetDigest(t, t->GetAttributeOrDie("Digest"));
@@ -355,7 +428,7 @@ static bool TestEVP(FileTest *t, KeyMap *key_map) {
355428
bssl::ScopedEVP_MD_CTX ctx, copy;
356429
EVP_PKEY_CTX *pctx;
357430
if (!md_op_init(ctx.get(), &pctx, digest, nullptr, key) ||
358-
!SetupContext(t, pctx) ||
431+
!SetupContext(t, key_map, pctx) ||
359432
!EVP_MD_CTX_copy_ex(copy.get(), ctx.get())) {
360433
return false;
361434
}
@@ -402,7 +475,7 @@ static bool TestEVP(FileTest *t, KeyMap *key_map) {
402475
!key_op_init(ctx.get()) ||
403476
(digest != nullptr &&
404477
!EVP_PKEY_CTX_set_signature_md(ctx.get(), digest)) ||
405-
!SetupContext(t, ctx.get())) {
478+
!SetupContext(t, key_map, ctx.get())) {
406479
return false;
407480
}
408481

@@ -436,7 +509,7 @@ static bool TestEVP(FileTest *t, KeyMap *key_map) {
436509
!EVP_PKEY_decrypt_init(decrypt_ctx.get()) ||
437510
(digest != nullptr &&
438511
!EVP_PKEY_CTX_set_signature_md(decrypt_ctx.get(), digest)) ||
439-
!SetupContext(t, decrypt_ctx.get()) ||
512+
!SetupContext(t, key_map, decrypt_ctx.get()) ||
440513
!EVP_PKEY_decrypt(decrypt_ctx.get(), nullptr, &plaintext_len,
441514
actual.data(), actual.size())) {
442515
return false;
@@ -456,7 +529,7 @@ static bool TestEVP(FileTest *t, KeyMap *key_map) {
456529
!EVP_PKEY_verify_init(verify_ctx.get()) ||
457530
(digest != nullptr &&
458531
!EVP_PKEY_CTX_set_signature_md(verify_ctx.get(), digest)) ||
459-
!SetupContext(t, verify_ctx.get())) {
532+
!SetupContext(t, key_map, verify_ctx.get())) {
460533
return false;
461534
}
462535
if (t->HasAttribute("VerifyPSSSaltLength")) {

crypto/evp/evp_tests.txt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1607,3 +1607,42 @@ Verify = Ed25519
16071607
Input = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
16081608
Output = e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b
16091609
Error = OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE
1610+
1611+
1612+
# Derive tests.
1613+
1614+
PrivateKey = ECDH-P256-Private
1615+
Type = EC
1616+
Input = 3041020100301306072a8648ce3d020106082a8648ce3d0301070427302502010104207d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534
1617+
1618+
PublicKey = ECDH-P256-Peer
1619+
Type = EC
1620+
Input = 3059301306072a8648ce3d020106082a8648ce3d03010703420004700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac
1621+
1622+
Derive = ECDH-P256-Private
1623+
DerivePeer = ECDH-P256-Peer
1624+
Output = 46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b
1625+
SmallBufferTruncates
1626+
1627+
PrivateKey = X25519-Private
1628+
Type = X25519
1629+
Input = 302e020100300506032b656e04220420a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4
1630+
ExpectRawPrivate = a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4
1631+
1632+
PublicKey = X25519-Peer
1633+
Type = X25519
1634+
Input = 302a300506032b656e032100e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c
1635+
ExpectRawPublic = e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c
1636+
1637+
PublicKey = X25519-SmallOrderPeer
1638+
Type = X25519
1639+
ExpectRawPublic = e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800
1640+
Input = 302a300506032b656e032100e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800
1641+
1642+
Derive = X25519-Private
1643+
DerivePeer = X25519-Peer
1644+
Output = c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552
1645+
1646+
Derive = X25519-Private
1647+
DerivePeer = X25519-SmallOrderPeer
1648+
Error = INVALID_PEER_KEY

crypto/evp/internal.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,14 +244,22 @@ typedef struct {
244244
char has_private;
245245
} ED25519_KEY;
246246

247+
typedef struct {
248+
uint8_t pub[32];
249+
uint8_t priv[32];
250+
char has_private;
251+
} X25519_KEY;
252+
247253
extern const EVP_PKEY_ASN1_METHOD dsa_asn1_meth;
248254
extern const EVP_PKEY_ASN1_METHOD ec_asn1_meth;
249255
extern const EVP_PKEY_ASN1_METHOD rsa_asn1_meth;
250256
extern const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth;
257+
extern const EVP_PKEY_ASN1_METHOD x25519_asn1_meth;
251258

252259
extern const EVP_PKEY_METHOD rsa_pkey_meth;
253260
extern const EVP_PKEY_METHOD ec_pkey_meth;
254261
extern const EVP_PKEY_METHOD ed25519_pkey_meth;
262+
extern const EVP_PKEY_METHOD x25519_pkey_meth;
255263

256264

257265
#if defined(__cplusplus)

crypto/evp/p_x25519.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/* Copyright (c) 2019, Google Inc.
2+
*
3+
* Permission to use, copy, modify, and/or distribute this software for any
4+
* purpose with or without fee is hereby granted, provided that the above
5+
* copyright notice and this permission notice appear in all copies.
6+
*
7+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10+
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12+
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13+
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14+
15+
#include <openssl/evp.h>
16+
17+
#include <openssl/curve25519.h>
18+
#include <openssl/err.h>
19+
#include <openssl/mem.h>
20+
21+
#include "internal.h"
22+
23+
24+
// X25519 has no parameters to copy.
25+
static int pkey_x25519_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) { return 1; }
26+
27+
static int pkey_x25519_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
28+
X25519_KEY *key = OPENSSL_malloc(sizeof(X25519_KEY));
29+
if (key == NULL) {
30+
OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
31+
return 0;
32+
}
33+
34+
if (!EVP_PKEY_set_type(pkey, EVP_PKEY_X25519)) {
35+
OPENSSL_free(key);
36+
return 0;
37+
}
38+
39+
X25519_keypair(key->pub, key->priv);
40+
key->has_private = 1;
41+
42+
OPENSSL_free(pkey->pkey.ptr);
43+
pkey->pkey.ptr = key;
44+
return 1;
45+
}
46+
47+
static int pkey_x25519_derive(EVP_PKEY_CTX *ctx, uint8_t *out,
48+
size_t *out_len) {
49+
if (ctx->pkey == NULL || ctx->peerkey == NULL) {
50+
OPENSSL_PUT_ERROR(EVP, EVP_R_KEYS_NOT_SET);
51+
return 0;
52+
}
53+
54+
const X25519_KEY *our_key = ctx->pkey->pkey.ptr;
55+
const X25519_KEY *peer_key = ctx->peerkey->pkey.ptr;
56+
if (our_key == NULL || peer_key == NULL) {
57+
OPENSSL_PUT_ERROR(EVP, EVP_R_KEYS_NOT_SET);
58+
return 0;
59+
}
60+
61+
if (!our_key->has_private) {
62+
OPENSSL_PUT_ERROR(EVP, EVP_R_NOT_A_PRIVATE_KEY);
63+
return 0;
64+
}
65+
66+
if (out != NULL) {
67+
if (*out_len < 32) {
68+
OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
69+
return 0;
70+
}
71+
if (!X25519(out, our_key->priv, peer_key->pub)) {
72+
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY);
73+
return 0;
74+
}
75+
}
76+
77+
*out_len = 32;
78+
return 1;
79+
}
80+
81+
static int pkey_x25519_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) {
82+
switch (type) {
83+
case EVP_PKEY_CTRL_PEER_KEY:
84+
// |EVP_PKEY_derive_set_peer| requires the key implement this command,
85+
// even if it is a no-op.
86+
return 1;
87+
88+
default:
89+
OPENSSL_PUT_ERROR(EVP, EVP_R_COMMAND_NOT_SUPPORTED);
90+
return 0;
91+
}
92+
}
93+
94+
const EVP_PKEY_METHOD x25519_pkey_meth = {
95+
EVP_PKEY_X25519,
96+
NULL /* init */,
97+
pkey_x25519_copy,
98+
NULL /* cleanup */,
99+
pkey_x25519_keygen,
100+
NULL /* sign */,
101+
NULL /* sign_message */,
102+
NULL /* verify */,
103+
NULL /* verify_message */,
104+
NULL /* verify_recover */,
105+
NULL /* encrypt */,
106+
NULL /* decrypt */,
107+
pkey_x25519_derive,
108+
NULL /* paramgen */,
109+
pkey_x25519_ctrl,
110+
};

0 commit comments

Comments
 (0)