Skip to content

Commit f72975f

Browse files
davidbenrvagg
authored andcommitted
crypto: emulate OpenSSL 1.0 ticket scheme in 1.1
OpenSSL 1.0.x used a 48-byte ticket key, but OpenSSL 1.1.x made it larger by using a larger HMAC-SHA256 key and using AES-256-CBC to encrypt. However, Node's public API exposes the 48-byte key. Implement the ticket key callback to restore the OpenSSL 1.0.x behavior. PR-URL: nodejs#16130 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Rod Vagg <rod@vagg.org>
1 parent 00a55e6 commit f72975f

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

src/node_crypto.cc

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,19 @@ void SecureContext::Init(const FunctionCallbackInfo<Value>& args) {
608608
SSL_SESS_CACHE_NO_AUTO_CLEAR);
609609
SSL_CTX_sess_set_get_cb(sc->ctx_, SSLWrap<Connection>::GetSessionCallback);
610610
SSL_CTX_sess_set_new_cb(sc->ctx_, SSLWrap<Connection>::NewSessionCallback);
611+
612+
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
613+
// OpenSSL 1.1.0 changed the ticket key size, but the OpenSSL 1.0.x size was
614+
// exposed in the public API. To retain compatibility, install a callback
615+
// which restores the old algorithm.
616+
if (RAND_bytes(sc->ticket_key_name_, sizeof(sc->ticket_key_name_)) <= 0 ||
617+
RAND_bytes(sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_)) <= 0 ||
618+
RAND_bytes(sc->ticket_key_aes_, sizeof(sc->ticket_key_aes_)) <= 0) {
619+
return env->ThrowError("Error generating ticket keys");
620+
}
621+
SSL_CTX_set_tlsext_ticket_key_cb(sc->ctx_,
622+
SecureContext::TicketCompatibilityCallback);
623+
#endif
611624
}
612625

613626

@@ -1285,11 +1298,17 @@ void SecureContext::GetTicketKeys(const FunctionCallbackInfo<Value>& args) {
12851298
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
12861299

12871300
Local<Object> buff = Buffer::New(wrap->env(), 48).ToLocalChecked();
1301+
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
1302+
memcpy(Buffer::Data(buff), wrap->ticket_key_name_, 16);
1303+
memcpy(Buffer::Data(buff) + 16, wrap->ticket_key_hmac_, 16);
1304+
memcpy(Buffer::Data(buff) + 32, wrap->ticket_key_aes_, 16);
1305+
#else
12881306
if (SSL_CTX_get_tlsext_ticket_keys(wrap->ctx_,
12891307
Buffer::Data(buff),
12901308
Buffer::Length(buff)) != 1) {
12911309
return wrap->env()->ThrowError("Failed to fetch tls ticket keys");
12921310
}
1311+
#endif
12931312

12941313
args.GetReturnValue().Set(buff);
12951314
#endif // !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys)
@@ -1312,11 +1331,17 @@ void SecureContext::SetTicketKeys(const FunctionCallbackInfo<Value>& args) {
13121331
return env->ThrowTypeError("Ticket keys length must be 48 bytes");
13131332
}
13141333

1334+
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
1335+
memcpy(wrap->ticket_key_name_, Buffer::Data(args[0]), 16);
1336+
memcpy(wrap->ticket_key_hmac_, Buffer::Data(args[0]) + 16, 16);
1337+
memcpy(wrap->ticket_key_aes_, Buffer::Data(args[0]) + 32, 16);
1338+
#else
13151339
if (SSL_CTX_set_tlsext_ticket_keys(wrap->ctx_,
13161340
Buffer::Data(args[0]),
13171341
Buffer::Length(args[0])) != 1) {
13181342
return env->ThrowError("Failed to fetch tls ticket keys");
13191343
}
1344+
#endif
13201345

13211346
args.GetReturnValue().Set(true);
13221347
#endif // !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys)
@@ -1427,6 +1452,42 @@ int SecureContext::TicketKeyCallback(SSL* ssl,
14271452
}
14281453

14291454

1455+
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
1456+
int SecureContext::TicketCompatibilityCallback(SSL* ssl,
1457+
unsigned char* name,
1458+
unsigned char* iv,
1459+
EVP_CIPHER_CTX* ectx,
1460+
HMAC_CTX* hctx,
1461+
int enc) {
1462+
SecureContext* sc = static_cast<SecureContext*>(
1463+
SSL_CTX_get_app_data(SSL_get_SSL_CTX(ssl)));
1464+
1465+
if (enc) {
1466+
memcpy(name, sc->ticket_key_name_, sizeof(sc->ticket_key_name_));
1467+
if (RAND_bytes(iv, 16) <= 0 ||
1468+
EVP_EncryptInit_ex(ectx, EVP_aes_128_cbc(), nullptr,
1469+
sc->ticket_key_aes_, iv) <= 0 ||
1470+
HMAC_Init_ex(hctx, sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_),
1471+
EVP_sha256(), nullptr) <= 0) {
1472+
return -1;
1473+
}
1474+
return 1;
1475+
}
1476+
1477+
if (memcmp(name, sc->ticket_key_name_, sizeof(sc->ticket_key_name_)) != 0) {
1478+
// The ticket key name does not match. Discard the ticket.
1479+
return 0;
1480+
}
1481+
1482+
if (EVP_DecryptInit_ex(ectx, EVP_aes_128_cbc(), nullptr, sc->ticket_key_aes_,
1483+
iv) <= 0 ||
1484+
HMAC_Init_ex(hctx, sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_),
1485+
EVP_sha256(), nullptr) <= 0) {
1486+
return -1;
1487+
}
1488+
return 1;
1489+
}
1490+
#endif
14301491

14311492

14321493
void SecureContext::CtxGetter(Local<String> property,

src/node_crypto.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ class SecureContext : public BaseObject {
103103
static const int kTicketKeyNameIndex = 3;
104104
static const int kTicketKeyIVIndex = 4;
105105

106+
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
107+
unsigned char ticket_key_name_[16];
108+
unsigned char ticket_key_aes_[16];
109+
unsigned char ticket_key_hmac_[16];
110+
#endif
111+
106112
protected:
107113
#if OPENSSL_VERSION_NUMBER < 0x10100000L
108114
static const int64_t kExternalSize = sizeof(SSL_CTX);
@@ -148,6 +154,15 @@ class SecureContext : public BaseObject {
148154
HMAC_CTX* hctx,
149155
int enc);
150156

157+
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
158+
static int TicketCompatibilityCallback(SSL* ssl,
159+
unsigned char* name,
160+
unsigned char* iv,
161+
EVP_CIPHER_CTX* ectx,
162+
HMAC_CTX* hctx,
163+
int enc);
164+
#endif
165+
151166
SecureContext(Environment* env, v8::Local<v8::Object> wrap)
152167
: BaseObject(env, wrap),
153168
ctx_(nullptr),

0 commit comments

Comments
 (0)