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: https://github.com/nodejs/node/pull/16130 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Rod Vagg <rod@vagg.org>
This commit is contained in:
parent
00a55e6287
commit
f72975ffa2
@ -608,6 +608,19 @@ void SecureContext::Init(const FunctionCallbackInfo<Value>& args) {
|
|||||||
SSL_SESS_CACHE_NO_AUTO_CLEAR);
|
SSL_SESS_CACHE_NO_AUTO_CLEAR);
|
||||||
SSL_CTX_sess_set_get_cb(sc->ctx_, SSLWrap<Connection>::GetSessionCallback);
|
SSL_CTX_sess_set_get_cb(sc->ctx_, SSLWrap<Connection>::GetSessionCallback);
|
||||||
SSL_CTX_sess_set_new_cb(sc->ctx_, SSLWrap<Connection>::NewSessionCallback);
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1285,11 +1298,17 @@ void SecureContext::GetTicketKeys(const FunctionCallbackInfo<Value>& args) {
|
|||||||
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
|
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
|
||||||
|
|
||||||
Local<Object> buff = Buffer::New(wrap->env(), 48).ToLocalChecked();
|
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_,
|
if (SSL_CTX_get_tlsext_ticket_keys(wrap->ctx_,
|
||||||
Buffer::Data(buff),
|
Buffer::Data(buff),
|
||||||
Buffer::Length(buff)) != 1) {
|
Buffer::Length(buff)) != 1) {
|
||||||
return wrap->env()->ThrowError("Failed to fetch tls ticket keys");
|
return wrap->env()->ThrowError("Failed to fetch tls ticket keys");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
args.GetReturnValue().Set(buff);
|
args.GetReturnValue().Set(buff);
|
||||||
#endif // !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys)
|
#endif // !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys)
|
||||||
@ -1312,11 +1331,17 @@ void SecureContext::SetTicketKeys(const FunctionCallbackInfo<Value>& args) {
|
|||||||
return env->ThrowTypeError("Ticket keys length must be 48 bytes");
|
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_,
|
if (SSL_CTX_set_tlsext_ticket_keys(wrap->ctx_,
|
||||||
Buffer::Data(args[0]),
|
Buffer::Data(args[0]),
|
||||||
Buffer::Length(args[0])) != 1) {
|
Buffer::Length(args[0])) != 1) {
|
||||||
return env->ThrowError("Failed to fetch tls ticket keys");
|
return env->ThrowError("Failed to fetch tls ticket keys");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
args.GetReturnValue().Set(true);
|
args.GetReturnValue().Set(true);
|
||||||
#endif // !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys)
|
#endif // !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys)
|
||||||
@ -1427,6 +1452,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,
|
void SecureContext::CtxGetter(Local<String> property,
|
||||||
|
@ -103,6 +103,12 @@ class SecureContext : public BaseObject {
|
|||||||
static const int kTicketKeyNameIndex = 3;
|
static const int kTicketKeyNameIndex = 3;
|
||||||
static const int kTicketKeyIVIndex = 4;
|
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:
|
protected:
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||||
static const int64_t kExternalSize = sizeof(SSL_CTX);
|
static const int64_t kExternalSize = sizeof(SSL_CTX);
|
||||||
@ -148,6 +154,15 @@ class SecureContext : public BaseObject {
|
|||||||
HMAC_CTX* hctx,
|
HMAC_CTX* hctx,
|
||||||
int enc);
|
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)
|
SecureContext(Environment* env, v8::Local<v8::Object> wrap)
|
||||||
: BaseObject(env, wrap),
|
: BaseObject(env, wrap),
|
||||||
ctx_(nullptr),
|
ctx_(nullptr),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user