Skip to content

Commit

Permalink
crypto: emulate OpenSSL 1.0 ticket scheme in 1.1
Browse files Browse the repository at this point in the history
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: #16130
Backport-PR-URL: #18622
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Rod Vagg <rod@vagg.org>
  • Loading branch information
davidben authored and gibfahn committed Feb 18, 2018
1 parent 4b90576 commit f9a597a
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 0 deletions.
61 changes: 61 additions & 0 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,19 @@ void SecureContext::Init(const FunctionCallbackInfo<Value>& args) {
SSL_SESS_CACHE_NO_AUTO_CLEAR);
SSL_CTX_sess_set_get_cb(sc->ctx_, SSLWrap<Connection>::GetSessionCallback);
SSL_CTX_sess_set_new_cb(sc->ctx_, SSLWrap<Connection>::NewSessionCallback);

#if OPENSSL_VERSION_NUMBER >= 0x10100000L
// OpenSSL 1.1.0 changed the ticket key size, but the OpenSSL 1.0.x size was
// exposed in the public API. To retain compatibility, install a callback
// which restores the old algorithm.
if (RAND_bytes(sc->ticket_key_name_, sizeof(sc->ticket_key_name_)) <= 0 ||
RAND_bytes(sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_)) <= 0 ||
RAND_bytes(sc->ticket_key_aes_, sizeof(sc->ticket_key_aes_)) <= 0) {
return env->ThrowError("Error generating ticket keys");
}
SSL_CTX_set_tlsext_ticket_key_cb(sc->ctx_,
SecureContext::TicketCompatibilityCallback);
#endif
}


Expand Down Expand Up @@ -1288,11 +1301,17 @@ void SecureContext::GetTicketKeys(const FunctionCallbackInfo<Value>& args) {
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());

Local<Object> buff = Buffer::New(wrap->env(), 48).ToLocalChecked();
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
memcpy(Buffer::Data(buff), wrap->ticket_key_name_, 16);
memcpy(Buffer::Data(buff) + 16, wrap->ticket_key_hmac_, 16);
memcpy(Buffer::Data(buff) + 32, wrap->ticket_key_aes_, 16);
#else
if (SSL_CTX_get_tlsext_ticket_keys(wrap->ctx_,
Buffer::Data(buff),
Buffer::Length(buff)) != 1) {
return wrap->env()->ThrowError("Failed to fetch tls ticket keys");
}
#endif

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

#if OPENSSL_VERSION_NUMBER >= 0x10100000L
memcpy(wrap->ticket_key_name_, Buffer::Data(args[0]), 16);
memcpy(wrap->ticket_key_hmac_, Buffer::Data(args[0]) + 16, 16);
memcpy(wrap->ticket_key_aes_, Buffer::Data(args[0]) + 32, 16);
#else
if (SSL_CTX_set_tlsext_ticket_keys(wrap->ctx_,
Buffer::Data(args[0]),
Buffer::Length(args[0])) != 1) {
return env->ThrowError("Failed to fetch tls ticket keys");
}
#endif

args.GetReturnValue().Set(true);
#endif // !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys)
Expand Down Expand Up @@ -1430,6 +1455,42 @@ int SecureContext::TicketKeyCallback(SSL* ssl,
}


#if OPENSSL_VERSION_NUMBER >= 0x10100000L
int SecureContext::TicketCompatibilityCallback(SSL* ssl,
unsigned char* name,
unsigned char* iv,
EVP_CIPHER_CTX* ectx,
HMAC_CTX* hctx,
int enc) {
SecureContext* sc = static_cast<SecureContext*>(
SSL_CTX_get_app_data(SSL_get_SSL_CTX(ssl)));

if (enc) {
memcpy(name, sc->ticket_key_name_, sizeof(sc->ticket_key_name_));
if (RAND_bytes(iv, 16) <= 0 ||
EVP_EncryptInit_ex(ectx, EVP_aes_128_cbc(), nullptr,
sc->ticket_key_aes_, iv) <= 0 ||
HMAC_Init_ex(hctx, sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_),
EVP_sha256(), nullptr) <= 0) {
return -1;
}
return 1;
}

if (memcmp(name, sc->ticket_key_name_, sizeof(sc->ticket_key_name_)) != 0) {
// The ticket key name does not match. Discard the ticket.
return 0;
}

if (EVP_DecryptInit_ex(ectx, EVP_aes_128_cbc(), nullptr, sc->ticket_key_aes_,
iv) <= 0 ||
HMAC_Init_ex(hctx, sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_),
EVP_sha256(), nullptr) <= 0) {
return -1;
}
return 1;
}
#endif


void SecureContext::CtxGetter(Local<String> property,
Expand Down
15 changes: 15 additions & 0 deletions src/node_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ class SecureContext : public BaseObject {
static const int kTicketKeyNameIndex = 3;
static const int kTicketKeyIVIndex = 4;

#if OPENSSL_VERSION_NUMBER >= 0x10100000L
unsigned char ticket_key_name_[16];
unsigned char ticket_key_aes_[16];
unsigned char ticket_key_hmac_[16];
#endif

protected:
#if OPENSSL_VERSION_NUMBER < 0x10100000L
static const int64_t kExternalSize = sizeof(SSL_CTX);
Expand Down Expand Up @@ -148,6 +154,15 @@ class SecureContext : public BaseObject {
HMAC_CTX* hctx,
int enc);

#if OPENSSL_VERSION_NUMBER >= 0x10100000L
static int TicketCompatibilityCallback(SSL* ssl,
unsigned char* name,
unsigned char* iv,
EVP_CIPHER_CTX* ectx,
HMAC_CTX* hctx,
int enc);
#endif

SecureContext(Environment* env, v8::Local<v8::Object> wrap)
: BaseObject(env, wrap),
ctx_(nullptr),
Expand Down

0 comments on commit f9a597a

Please sign in to comment.