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_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
|
||||
}
|
||||
|
||||
|
||||
@ -1285,11 +1298,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)
|
||||
@ -1312,11 +1331,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)
|
||||
@ -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,
|
||||
|
@ -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);
|
||||
@ -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),
|
||||
|
Loading…
x
Reference in New Issue
Block a user