Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDenys Otrishko <shishugi@gmail.com>2018-10-29 11:38:43 +0300
committerRuben Bridgewater <ruben@bridgewater.de>2019-12-25 14:24:42 +0300
commitf8d7e2216e5821719b5a341c41251d5a860cf5f7 (patch)
tree8f3a1db4b065a844483ee1a8e388702463683570 /src
parent3d47c8592d179991d3bfa4902f12c4fce07ac2d3 (diff)
tls: add PSK support
Add the `pskCallback` client/server option, which resolves an identity or identity hint to a pre-shared key. Add the `pskIdentityHint` server option to set the identity hint for the ServerKeyExchange message. Co-authored-by: Chris Osborn <chris.osborn@sitelier.com> Co-authored-by: stephank <gh@stephank.nl> Co-authored-by: Taylor Zane Glaeser <tzglaeser@gmail.com> PR-URL: https://github.com/nodejs/node/pull/23188 Reviewed-By: Sam Roberts <vieuxtech@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
Diffstat (limited to 'src')
-rw-r--r--src/env.h13
-rw-r--r--src/node_crypto.cc10
-rw-r--r--src/node_errors.h2
-rw-r--r--src/tls_wrap.cc134
-rw-r--r--src/tls_wrap.h17
5 files changed, 171 insertions, 5 deletions
diff --git a/src/env.h b/src/env.h
index c86f0f7de7b..150cdf0fb1e 100644
--- a/src/env.h
+++ b/src/env.h
@@ -160,11 +160,12 @@ constexpr size_t kFsStatsBufferLength =
// Symbols are per-isolate primitives but Environment proxies them
// for the sake of convenience.
-#define PER_ISOLATE_SYMBOL_PROPERTIES(V) \
- V(handle_onclose_symbol, "handle_onclose") \
- V(no_message_symbol, "no_message_symbol") \
- V(oninit_symbol, "oninit") \
- V(owner_symbol, "owner") \
+#define PER_ISOLATE_SYMBOL_PROPERTIES(V) \
+ V(handle_onclose_symbol, "handle_onclose") \
+ V(no_message_symbol, "no_message_symbol") \
+ V(oninit_symbol, "oninit") \
+ V(owner_symbol, "owner") \
+ V(onpskexchange_symbol, "onpskexchange") \
// Strings are per-isolate primitives but Environment proxies them
// for the sake of convenience. Strings should be ASCII-only.
@@ -254,6 +255,7 @@ constexpr size_t kFsStatsBufferLength =
V(host_string, "host") \
V(hostmaster_string, "hostmaster") \
V(http_1_1_string, "http/1.1") \
+ V(identity_string, "identity") \
V(ignore_string, "ignore") \
V(import_string, "import") \
V(infoaccess_string, "infoAccess") \
@@ -325,6 +327,7 @@ constexpr size_t kFsStatsBufferLength =
V(priority_string, "priority") \
V(process_string, "process") \
V(promise_string, "promise") \
+ V(psk_string, "psk") \
V(pubkey_string, "pubkey") \
V(query_string, "query") \
V(raw_string, "raw") \
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index 87a3e5525d1..9165e9171e6 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -2620,6 +2620,16 @@ void SSLWrap<Base>::VerifyError(const FunctionCallbackInfo<Value>& args) {
if (X509* peer_cert = SSL_get_peer_certificate(w->ssl_.get())) {
X509_free(peer_cert);
x509_verify_error = SSL_get_verify_result(w->ssl_.get());
+ } else {
+ const SSL_CIPHER* curr_cipher = SSL_get_current_cipher(w->ssl_.get());
+ const SSL_SESSION* sess = SSL_get_session(w->ssl_.get());
+ // Allow no-cert for PSK authentication in TLS1.2 and lower.
+ // In TLS1.3 check that session was reused because TLS1.3 PSK
+ // looks like session resumption. Is there a better way?
+ if (SSL_CIPHER_get_auth_nid(curr_cipher) == NID_auth_psk ||
+ (SSL_SESSION_get_protocol_version(sess) == TLS1_3_VERSION &&
+ SSL_session_reused(w->ssl_.get())))
+ return args.GetReturnValue().SetNull();
}
if (x509_verify_error == X509_V_OK)
diff --git a/src/node_errors.h b/src/node_errors.h
index 6080aa93dba..74413e7456a 100644
--- a/src/node_errors.h
+++ b/src/node_errors.h
@@ -58,6 +58,7 @@ void PrintErrorString(const char* format, ...);
V(ERR_STRING_TOO_LONG, Error) \
V(ERR_TLS_INVALID_PROTOCOL_METHOD, TypeError) \
V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, TypeError) \
+ V(ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED, Error) \
#define V(code, type) \
inline v8::Local<v8::Value> code(v8::Isolate* isolate, \
@@ -101,6 +102,7 @@ void PrintErrorString(const char* format, ...);
"Script execution was interrupted by `SIGINT`") \
V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, \
"Cannot serialize externalized SharedArrayBuffer") \
+ V(ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED, "Failed to set PSK identity hint") \
#define V(code, message) \
inline v8::Local<v8::Value> code(v8::Isolate* isolate) { \
diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc
index cd7a5d59ebd..10ebc4ccd9a 100644
--- a/src/tls_wrap.cc
+++ b/src/tls_wrap.cc
@@ -28,6 +28,7 @@
#include "node_crypto_bio.h" // NodeBIO
// ClientHelloParser
#include "node_crypto_clienthello-inl.h"
+#include "node_errors.h"
#include "stream_base-inl.h"
#include "util-inl.h"
@@ -42,8 +43,11 @@ using v8::Exception;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
+using v8::Integer;
using v8::Isolate;
using v8::Local;
+using v8::Maybe;
+using v8::MaybeLocal;
using v8::Object;
using v8::ReadOnly;
using v8::Signature;
@@ -1076,6 +1080,131 @@ int TLSWrap::SelectSNIContextCallback(SSL* s, int* ad, void* arg) {
return SSL_TLSEXT_ERR_OK;
}
+#ifndef OPENSSL_NO_PSK
+
+void TLSWrap::SetPskIdentityHint(const FunctionCallbackInfo<Value>& args) {
+ TLSWrap* p;
+ ASSIGN_OR_RETURN_UNWRAP(&p, args.Holder());
+ CHECK_NOT_NULL(p->ssl_);
+
+ Environment* env = p->env();
+ Isolate* isolate = env->isolate();
+
+ CHECK(args[0]->IsString());
+ node::Utf8Value hint(isolate, args[0].As<String>());
+
+ if (!SSL_use_psk_identity_hint(p->ssl_.get(), *hint)) {
+ Local<Value> err = node::ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED(isolate);
+ p->MakeCallback(env->onerror_string(), 1, &err);
+ }
+}
+
+void TLSWrap::EnablePskCallback(const FunctionCallbackInfo<Value>& args) {
+ TLSWrap* wrap;
+ ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
+ CHECK_NOT_NULL(wrap->ssl_);
+
+ SSL_set_psk_server_callback(wrap->ssl_.get(), PskServerCallback);
+ SSL_set_psk_client_callback(wrap->ssl_.get(), PskClientCallback);
+}
+
+unsigned int TLSWrap::PskServerCallback(SSL* s,
+ const char* identity,
+ unsigned char* psk,
+ unsigned int max_psk_len) {
+ TLSWrap* p = static_cast<TLSWrap*>(SSL_get_app_data(s));
+
+ Environment* env = p->env();
+ Isolate* isolate = env->isolate();
+ HandleScope scope(isolate);
+
+ MaybeLocal<String> maybe_identity_str =
+ v8::String::NewFromUtf8(isolate, identity, v8::NewStringType::kNormal);
+
+ v8::Local<v8::String> identity_str;
+ if (!maybe_identity_str.ToLocal(&identity_str)) return 0;
+
+ // Make sure there are no utf8 replacement symbols.
+ v8::String::Utf8Value identity_utf8(isolate, identity_str);
+ if (strcmp(*identity_utf8, identity) != 0) return 0;
+
+ Local<Value> argv[] = {identity_str,
+ Integer::NewFromUnsigned(isolate, max_psk_len)};
+
+ MaybeLocal<Value> maybe_psk_val =
+ p->MakeCallback(env->onpskexchange_symbol(), arraysize(argv), argv);
+ Local<Value> psk_val;
+ if (!maybe_psk_val.ToLocal(&psk_val) || !psk_val->IsArrayBufferView())
+ return 0;
+
+ char* psk_buf = Buffer::Data(psk_val);
+ size_t psk_buflen = Buffer::Length(psk_val);
+
+ if (psk_buflen > max_psk_len) return 0;
+
+ memcpy(psk, psk_buf, psk_buflen);
+ return psk_buflen;
+}
+
+unsigned int TLSWrap::PskClientCallback(SSL* s,
+ const char* hint,
+ char* identity,
+ unsigned int max_identity_len,
+ unsigned char* psk,
+ unsigned int max_psk_len) {
+ TLSWrap* p = static_cast<TLSWrap*>(SSL_get_app_data(s));
+
+ Environment* env = p->env();
+ Isolate* isolate = env->isolate();
+ HandleScope scope(isolate);
+
+ Local<Value> argv[] = {Null(isolate),
+ Integer::NewFromUnsigned(isolate, max_psk_len),
+ Integer::NewFromUnsigned(isolate, max_identity_len)};
+ if (hint != nullptr) {
+ MaybeLocal<String> maybe_hint = String::NewFromUtf8(isolate, hint);
+
+ Local<String> local_hint;
+ if (!maybe_hint.ToLocal(&local_hint)) return 0;
+
+ argv[0] = local_hint;
+ }
+ MaybeLocal<Value> maybe_ret =
+ p->MakeCallback(env->onpskexchange_symbol(), arraysize(argv), argv);
+ Local<Value> ret;
+ if (!maybe_ret.ToLocal(&ret) || !ret->IsObject()) return 0;
+ Local<Object> obj = ret.As<Object>();
+
+ MaybeLocal<Value> maybe_psk_val = obj->Get(env->context(), env->psk_string());
+
+ Local<Value> psk_val;
+ if (!maybe_psk_val.ToLocal(&psk_val) || !psk_val->IsArrayBufferView())
+ return 0;
+
+ char* psk_buf = Buffer::Data(psk_val);
+ size_t psk_buflen = Buffer::Length(psk_val);
+
+ if (psk_buflen > max_psk_len) return 0;
+
+ MaybeLocal<Value> maybe_identity_val =
+ obj->Get(env->context(), env->identity_string());
+ Local<Value> identity_val;
+ if (!maybe_identity_val.ToLocal(&identity_val) || !identity_val->IsString())
+ return 0;
+ Local<String> identity_str = identity_val.As<String>();
+
+ String::Utf8Value identity_buf(isolate, identity_str);
+ size_t identity_len = identity_buf.length();
+
+ if (identity_len > max_identity_len) return 0;
+
+ memcpy(identity, *identity_buf, identity_len);
+ memcpy(psk, psk_buf, psk_buflen);
+
+ return psk_buflen;
+}
+
+#endif
void TLSWrap::GetWriteQueueSize(const FunctionCallbackInfo<Value>& info) {
TLSWrap* wrap;
@@ -1142,6 +1271,11 @@ void TLSWrap::Initialize(Local<Object> target,
env->SetProtoMethod(t, "destroySSL", DestroySSL);
env->SetProtoMethod(t, "enableCertCb", EnableCertCb);
+#ifndef OPENSSL_NO_PSK
+ env->SetProtoMethod(t, "setPskIdentityHint", SetPskIdentityHint);
+ env->SetProtoMethod(t, "enablePskCallback", EnablePskCallback);
+#endif
+
StreamBase::AddMethods(env, t);
SSLWrap<TLSWrap>::AddMethods(env, t);
diff --git a/src/tls_wrap.h b/src/tls_wrap.h
index 14b7327e7d8..7bb33b4a3cb 100644
--- a/src/tls_wrap.h
+++ b/src/tls_wrap.h
@@ -169,6 +169,23 @@ class TLSWrap : public AsyncWrap,
static void SetServername(const v8::FunctionCallbackInfo<v8::Value>& args);
static int SelectSNIContextCallback(SSL* s, int* ad, void* arg);
+#ifndef OPENSSL_NO_PSK
+ static void SetPskIdentityHint(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void EnablePskCallback(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+ static unsigned int PskServerCallback(SSL* s,
+ const char* identity,
+ unsigned char* psk,
+ unsigned int max_psk_len);
+ static unsigned int PskClientCallback(SSL* s,
+ const char* hint,
+ char* identity,
+ unsigned int max_identity_len,
+ unsigned char* psk,
+ unsigned int max_psk_len);
+#endif
+
crypto::SecureContext* sc_;
// BIO buffers hold encrypted data.
BIO* enc_in_ = nullptr; // StreamListener fills this for SSL_read().