summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Benjamin <davidben@google.com>2017-09-23 02:55:19 -0400
committerRod Vagg <rod@vagg.org>2017-11-11 20:42:50 +1100
commitf72975ffa29b7f3a8d4210bf6b34ca625e6b29f2 (patch)
treeed9aa11712d57da1752d13fd2b17792264bca9a2
parent00a55e62877faab053dbbe1178488f7cf8dee466 (diff)
downloadandroid-node-v8-f72975ffa29b7f3a8d4210bf6b34ca625e6b29f2.tar.gz
android-node-v8-f72975ffa29b7f3a8d4210bf6b34ca625e6b29f2.tar.bz2
android-node-v8-f72975ffa29b7f3a8d4210bf6b34ca625e6b29f2.zip
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>
-rw-r--r--src/node_crypto.cc61
-rw-r--r--src/node_crypto.h15
2 files changed, 76 insertions, 0 deletions
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index 90280ae7b7..63b69b5e77 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -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,
diff --git a/src/node_crypto.h b/src/node_crypto.h
index c26bde0b8d..c3bc5d24c3 100644
--- a/src/node_crypto.h
+++ b/src/node_crypto.h
@@ -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),