Compare commits

...

153 Commits

Author SHA1 Message Date
Alistair Smith
7749bf784d glob srcs 2025-05-21 19:57:38 -07:00
Alistair Smith
2f635c423d Merge branch 'main' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-21 19:57:23 -07:00
Alistair Smith
53dd1f98c4 Merge branch 'main' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-19 16:10:49 -07:00
Alistair Smith
38686f4265 define consts earlier 2025-05-16 17:18:14 -07:00
Alistair Smith
372b0c3141 remove logs 2025-05-16 13:08:23 -07:00
Alistair Smith
3afa13b8e6 fix: properly map non min/max vers related error codes 2025-05-16 13:05:21 -07:00
Alistair Smith
40427855e6 debug 2025-05-16 11:34:37 -07:00
Alistair Smith
a1124d6888 Merge branch 'ali/tls-min-max-cli-and-test' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-16 11:17:41 -07:00
Alistair Smith
bf87b05272 check verify error code 2025-05-16 11:17:39 -07:00
Alistair Smith
29a8a45140 Merge branch 'main' into ali/tls-min-max-cli-and-test 2025-05-16 11:09:27 -07:00
Alistair Smith
447a9a61b0 debloat 2025-05-15 18:07:47 -07:00
Alistair Smith
0acdeddb68 some debloat 2025-05-15 18:05:01 -07:00
Alistair Smith
00e87016ca rm 2025-05-15 17:57:47 -07:00
Alistair Smith
bdeaea68d3 remove redundant bloat 2025-05-15 17:53:44 -07:00
Alistair Smith
5e267afdb7 2 failing 2025-05-15 17:44:27 -07:00
Alistair Smith
9317dc2822 try 2025-05-15 17:02:12 -07:00
Alistair Smith
2d57bb137e isolate failure 2025-05-15 16:56:14 -07:00
Alistair Smith
4a663f306e passing bar these 2025-05-15 16:50:47 -07:00
Alistair Smith
8786ff801a almost there 2025-05-15 16:47:05 -07:00
Alistair Smith
6ab9b1c64b secure protocol property plumbing 2025-05-15 16:42:50 -07:00
Alistair Smith
670d6b5454 debug 2025-05-15 16:26:54 -07:00
Alistair Smith
6154c5d254 define secure_protocol_method 2025-05-15 16:05:51 -07:00
Alistair Smith
a1b374b889 old/invalid protocol 2025-05-15 15:51:00 -07:00
Alistair Smith
234fa60dcf Almost all cases passing 2025-05-15 15:38:00 -07:00
Alistair Smith
447216a17f peek openssl error queue for real reason(🤦‍♂️) 2025-05-15 15:33:19 -07:00
Alistair Smith
f5047e92b5 debug 2025-05-15 15:21:39 -07:00
Alistair Smith
ceca54e1fe failing 2025-05-15 14:45:17 -07:00
Alistair Smith
f893b08ed2 passing 2025-05-15 14:36:28 -07:00
Alistair Smith
3d94247a61 restart 2025-05-15 14:33:27 -07:00
Alistair Smith
1b19c8c90c Merge branch 'ali/tls-min-max-cli-and-test' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-15 13:52:14 -07:00
Alistair Smith
b86a462e38 merge 2025-05-15 13:52:09 -07:00
Alistair Smith
10fc8ad203 Merge branch 'main' into ali/tls-min-max-cli-and-test 2025-05-14 21:54:38 -07:00
Alistair Smith
527e59d966 nice 2025-05-14 17:15:51 -07:00
Alistair Smith
4befd51593 Works 2025-05-14 17:13:55 -07:00
Alistair Smith
0257a24c7e debug 2025-05-14 17:13:17 -07:00
Alistair Smith
99a76fb896 checkpoint 2025-05-14 17:11:43 -07:00
Alistair Smith
18e34437d2 passing! 2025-05-14 16:56:13 -07:00
Alistair Smith
5fb30c8122 debugs 2025-05-14 16:27:21 -07:00
Alistair Smith
7f2b54e6ec for now two failings 2025-05-14 16:16:28 -07:00
Alistair Smith
2bbac60588 also passing 2025-05-14 16:08:51 -07:00
Alistair Smith
7d59378723 thse are all passing 2025-05-14 15:50:49 -07:00
Alistair Smith
ff7c0494fc Works up to here! 2025-05-14 15:49:14 -07:00
Alistair Smith
881a1f2dc2 Merge branch 'main' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-14 15:22:45 -07:00
Alistair Smith
1fed307743 broken 2025-05-14 15:22:40 -07:00
Alistair Smith
5154101e3a fix errno 2025-05-14 14:52:26 -07:00
Alistair Smith
f2be87e747 ssl test file, new err code 2025-05-14 14:26:20 -07:00
Alistair Smith
9268977b51 rejection test 2025-05-14 13:07:01 -07:00
Alistair Smith
a7e9b1e782 add more tests 2025-05-14 12:58:21 -07:00
Alistair Smith
c3bdf0edf8 fix it 2025-05-14 12:51:36 -07:00
Alistair Smith
382e848d44 isolate test, other debug 2025-05-14 10:47:14 -07:00
Alistair Smith
7a52e754b1 rm 2025-05-13 18:24:33 -07:00
Alistair Smith
2c91911991 hm 2025-05-13 16:59:29 -07:00
Alistair Smith
0c9208c712 changes 2025-05-13 16:04:57 -07:00
Alistair Smith
4b04480283 changes 2025-05-13 15:05:41 -07:00
Alistair Smith
67a26f0129 Merge branch 'main' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-13 14:19:12 -07:00
Alistair Smith
a257dc2403 debug 2025-05-13 14:14:43 -07:00
Alistair Smith
27dd1e8dfa rm 2025-05-13 13:51:39 -07:00
Alistair Smith
e91d0229d4 rm 2025-05-13 13:51:30 -07:00
Alistair Smith
5f65bf5785 debugging 2025-05-13 12:59:12 -07:00
Alistair Smith
5eec63b3b3 dont allow numerical assignment of min/max versions 2025-05-12 16:16:37 -07:00
Alistair Smith
ace91ce21f match node logic 2025-05-12 16:12:19 -07:00
Alistair Smith
cb05ed0715 net 2025-05-12 16:00:38 -07:00
Alistair Smith
f044b44db7 bun happy eyeballs message 2025-05-12 15:55:59 -07:00
Alistair Smith
aea96977b0 revert net.ts 2025-05-12 15:55:26 -07:00
Alistair Smith
763c01763a from node 2025-05-12 15:52:55 -07:00
Alistair Smith
210d4fc32a Merge branch 'main' into ali/tls-min-max-cli-and-test 2025-05-12 15:03:20 -07:00
Alistair Smith
043f24aa4b commiting to pair with ciro 2025-05-12 15:01:05 -07:00
Alistair Smith
a55e27abe1 fix: check min/max version for ssl context re-use 2025-05-12 12:14:53 -07:00
Alistair Smith
72eb99654f isolate test cases for now 2025-05-12 12:07:02 -07:00
Alistair Smith
2e6eaaeea0 Merge branch 'main' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-12 11:21:40 -07:00
Alistair Smith
2a36a4a1cb apply tls options 2025-05-12 11:21:32 -07:00
Alistair Smith
28a6ba92ed refactor out tls validation logic 2025-05-09 20:04:34 -07:00
Alistair Smith
7b64f89f2c fix imports uws.zig 2025-05-09 19:55:32 -07:00
Alistair Smith
0c72bb578e revert imports in server.zig 2025-05-09 19:54:39 -07:00
Alistair Smith
2cfec429f7 accidental removal 2025-05-09 19:52:00 -07:00
Alistair Smith
3512087684 Merge branch 'main' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-09 19:51:11 -07:00
Alistair Smith
ec5bb50501 revert cli.zig, apply tls changes 2025-05-09 19:50:58 -07:00
Alistair Smith
14fe8df3dc export tls 2025-05-09 19:40:35 -07:00
Alistair Smith
36cc379037 revert bun.zig 2025-05-09 19:37:46 -07:00
Alistair Smith
1ecd74ad2e call socket hang up 2025-05-09 19:30:43 -07:00
Alistair Smith
9b05d514b4 temporarily ignore socket hang up (lol) 2025-05-09 18:55:59 -07:00
Alistair Smith
7d8b741d66 Merge branch 'ali/tls-min-max-cli-and-test' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-09 18:42:28 -07:00
Alistair Smith
f8fd2c2a82 glob sources 2025-05-09 18:42:26 -07:00
alii
ef0c48a23d bun run prettier 2025-05-10 01:37:41 +00:00
Alistair Smith
d3fcad833b Merge branch 'main' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-09 18:35:41 -07:00
Alistair Smith
f7fc34dfaa Merge branch 'ali/tls-min-max-cli-and-test' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-09 17:37:09 -07:00
Alistair Smith
f3b769f1b9 fix tls exports 2025-05-09 17:36:24 -07:00
alii
2d04dcf697 bun run prettier 2025-05-10 00:30:48 +00:00
Alistair Smith
5bdc738b91 passes 2025-05-09 17:28:13 -07:00
alii
6f398a1924 bun run prettier 2025-05-09 23:57:02 +00:00
Alistair Smith
0b9cdc069b refactor tls version resolution 2025-05-09 16:54:28 -07:00
Alistair Smith
7d5ae23b2e Merge branch 'main' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-09 16:54:07 -07:00
Alistair Smith
2798379edf net changes, ERR_TLS_PROTOCOL_VERSION_CONFLICT fix message 2025-05-09 14:57:52 -07:00
Alistair Smith
d72e605ab2 Merge branch 'main' into ali/tls-min-max-cli-and-test 2025-05-09 14:08:33 -07:00
Alistair Smith
dfa4dee851 fix bun-types 2025-05-09 14:06:19 -07:00
Alistair Smith
74e369ec85 debug 2025-05-08 23:30:43 -07:00
Alistair Smith
f88344a61d Merge branch 'main' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-08 19:25:52 -07:00
Alistair Smith
be32a2f14a propogate err 2025-05-08 19:09:59 -07:00
Alistair Smith
df92e99e96 dont organize imports for zig 2025-05-08 17:59:27 -07:00
Alistair Smith
9e0bdfadfe update sources 2025-05-08 17:04:24 -07:00
Alistair Smith
f728d17346 Merge branch 'main' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-08 16:58:40 -07:00
Alistair Smith
94293cdf51 use ERR_TLS_INVALID_PROTOCOL_VERSION new signature 2025-05-08 16:53:39 -07:00
Alistair Smith
187dc46b71 passing 2025-05-08 16:45:19 -07:00
Alistair Smith
b3b276ba45 ticket keys test? 2025-05-08 16:28:06 -07:00
Alistair Smith
97324e2593 add min-version 1.0 test file 2025-05-08 16:22:57 -07:00
Alistair Smith
8ef431c4d9 Revert 2283341efd 2025-05-08 16:20:33 -07:00
Alistair Smith
0ecf930de2 change ERR_TLS_INVALID_PROTOCOL_VERSION builtin 2025-05-08 16:19:53 -07:00
Alistair Smith
164675cf7a use ERR_INVALID_ARG_TYPE for Buffer instance 2025-05-08 15:09:12 -07:00
Alistair Smith
2283341efd Support "instance of" in ERR_INVALID_ARG_TYPE
Co-authored-by: Meghan Denny <meghan@bun.sh>
2025-05-08 15:06:38 -07:00
Alistair Smith
3d25000052 rm secureprotocol 2025-05-08 15:04:27 -07:00
Alistair Smith
20911323df clearer naming for tls cli binding logic 2025-05-08 14:46:55 -07:00
Alistair Smith
a5a8966309 proper check for checkServerIdentity 2025-05-08 14:44:28 -07:00
Alistair Smith
416121f6a1 rm 2025-05-08 14:33:04 -07:00
Alistair Smith
629b67721f fix alpn 2025-05-08 14:32:55 -07:00
Alistair Smith
040b00dc7c rm 2025-05-08 14:15:40 -07:00
Alistair Smith
821848f130 more validation 2025-05-08 14:14:26 -07:00
Alistair Smith
da62a8984f housekeeping 2025-05-08 13:43:33 -07:00
Alistair Smith
de05784517 partially complete validation 2025-05-08 13:35:48 -07:00
Alistair Smith
5986566ab7 add validations test 2025-05-08 13:21:59 -07:00
Alistair Smith
8380a8fe6f rm debug logs 2025-05-08 13:07:16 -07:00
Alistair Smith
0626a0f6e6 Fix bad words count 2025-05-08 13:04:45 -07:00
Alistair Smith
d60da18a86 fix cli failing arg test 2025-05-08 12:59:58 -07:00
Alistair Smith
90be0a596d fix node cli args, other diff cleanup 2025-05-08 12:09:25 -07:00
Alistair Smith
d91ca54eec add min v1.1 test file 2025-05-08 11:19:47 -07:00
Alistair Smith
b6530ba6a6 2 more test files 2025-05-08 11:16:32 -07:00
Alistair Smith
8a22127721 2 more test files 2025-05-08 11:14:31 -07:00
Alistair Smith
38ff15ace2 rm 2025-05-08 11:05:05 -07:00
Alistair Smith
4c14c7e838 unused constants 2025-05-08 11:04:24 -07:00
Alistair Smith
6cb5e1ca80 revert openssl changes for dead path 2025-05-08 10:43:21 -07:00
Alistair Smith
5dbd814fc0 Merge branch 'main' of github.com:oven-sh/bun into ali/tls-min-max-cli-and-test 2025-05-08 10:08:03 -07:00
Alistair Smith
4037fe909a debug 2025-05-08 10:08:00 -07:00
Alistair Smith
ef6b9da947 nativeland changes 2025-05-07 23:32:36 -07:00
Alistair Smith
82a019a287 pass openssl values in nativeland 2025-05-07 23:23:44 -07:00
Alistair Smith
5b767a9b25 pass tls versions as openssl values 2025-05-07 23:16:47 -07:00
Alistair Smith
23fd3b8910 change how versions get passed 2025-05-07 23:04:25 -07:00
Alistair Smith
7c57c3c7da rm debug 2025-05-07 21:39:16 -07:00
Alistair Smith
40980510d8 debug 2025-05-07 21:39:09 -07:00
Alistair Smith
f32b6d0111 stuff 2025-05-07 21:26:56 -07:00
Alistair Smith
2baa668b37 default to 1.2 min 2025-05-07 18:02:41 -07:00
Alistair Smith
bd51d5ee87 do set min tls 2025-05-07 18:00:31 -07:00
Alistair Smith
aae850e7e2 catch unreachable; increased 2025-05-07 17:57:52 -07:00
Alistair Smith
14ea951eec assign tls versions 2025-05-07 17:49:48 -07:00
Alistair Smith
563b8f57fc consume values 2025-05-07 17:45:56 -07:00
Alistair Smith
7b0e470c0a changes 2025-05-07 17:26:29 -07:00
Alistair Smith
4f1233eee6 fix test impl 2025-05-07 17:19:04 -07:00
Alistair Smith
033264200c more coverage, and slightly related ProcessEnv should not extend ImportMetaEnv 2025-05-07 17:15:55 -07:00
Alistair Smith
90594260d1 add the other minmax test 2025-05-07 17:02:19 -07:00
Alistair Smith
b6ea92f555 more 2025-05-07 16:54:52 -07:00
Alistair Smith
37a55d21de more zig 2025-05-07 16:39:34 -07:00
Alistair Smith
addc35784b basic wireup of tls min/max 2025-05-07 16:15:40 -07:00
Alistair Smith
87c520dc27 Specify all tls versions according to Node 2025-05-07 15:45:05 -07:00
Alistair Smith
35152da184 errors 2025-05-07 15:37:06 -07:00
Alistair Smith
d6d14140a4 add test file 2025-05-07 15:26:14 -07:00
40 changed files with 1561 additions and 134 deletions

5
.gitignore vendored
View File

@@ -183,4 +183,7 @@ codegen-for-zig-team.tar.gz
*.sock
scratch*.{js,ts,tsx,cjs,mjs}
*.bun-build
*.bun-build
**/.claude/settings.local.json
/.tmp

View File

@@ -39,6 +39,7 @@
"zig.zls.path": "${workspaceFolder}/vendor/zig/zls.exe",
"zig.formattingProvider": "zls",
"zig.zls.enableInlayHints": false,
"[zig]": {
"editor.tabSize": 4,
"editor.useTabStops": false,

View File

@@ -408,8 +408,8 @@ src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA1.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA224.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA256.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA384.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmX25519.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA512.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmX25519.cpp
src/bun.js/bindings/webcrypto/CryptoDigest.cpp
src/bun.js/bindings/webcrypto/CryptoKey.cpp
src/bun.js/bindings/webcrypto/CryptoKeyAES.cpp
@@ -443,7 +443,6 @@ src/bun.js/bindings/webcrypto/JSHkdfParams.cpp
src/bun.js/bindings/webcrypto/JSHmacKeyParams.cpp
src/bun.js/bindings/webcrypto/JSJsonWebKey.cpp
src/bun.js/bindings/webcrypto/JSPbkdf2Params.cpp
src/bun.js/bindings/webcrypto/JSX25519Params.cpp
src/bun.js/bindings/webcrypto/JSRsaHashedImportParams.cpp
src/bun.js/bindings/webcrypto/JSRsaHashedKeyGenParams.cpp
src/bun.js/bindings/webcrypto/JSRsaKeyGenParams.cpp
@@ -451,6 +450,7 @@ src/bun.js/bindings/webcrypto/JSRsaOaepParams.cpp
src/bun.js/bindings/webcrypto/JSRsaOtherPrimesInfo.cpp
src/bun.js/bindings/webcrypto/JSRsaPssParams.cpp
src/bun.js/bindings/webcrypto/JSSubtleCrypto.cpp
src/bun.js/bindings/webcrypto/JSX25519Params.cpp
src/bun.js/bindings/webcrypto/OpenSSLUtilities.cpp
src/bun.js/bindings/webcrypto/PhonyWorkQueue.cpp
src/bun.js/bindings/webcrypto/SerializedCryptoKeyWrapOpenSSL.cpp

View File

@@ -164,6 +164,7 @@ src/bun.js/node/node_http_binding.zig
src/bun.js/node/node_net_binding.zig
src/bun.js/node/node_os.zig
src/bun.js/node/node_process.zig
src/bun.js/node/node_tls_binding.zig
src/bun.js/node/node_util_binding.zig
src/bun.js/node/node_zlib_binding.zig
src/bun.js/node/nodejs_error_code.zig
@@ -581,6 +582,7 @@ src/system_timer.zig
src/test/fixtures.zig
src/test/recover.zig
src/thread_pool.zig
src/tls.zig
src/tmp.zig
src/toml/toml_lexer.zig
src/toml/toml_parser.zig

View File

@@ -2,7 +2,7 @@ export {};
declare global {
namespace NodeJS {
interface ProcessEnv extends Bun.Env, ImportMetaEnv {}
interface ProcessEnv extends Bun.Env {}
interface Process {
readonly version: string;

View File

@@ -66,6 +66,7 @@ struct loop_ssl_data {
struct us_internal_ssl_socket_context_t {
struct us_socket_context_t sc;
struct us_bun_socket_context_options_t options;
// this thing can be shared with other socket contexts via socket transfer!
// maybe instead of holding once you hold many, a vector or set
@@ -285,7 +286,6 @@ int us_internal_handle_shutdown(struct us_internal_ssl_socket_t *s, int force_fa
// we got some error here, but we dont care about it, we are closing the socket
int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
// clear
ERR_clear_error();
s->fatal_error = 1;
// Fatal error occurred, we should close the socket imeadiatly
@@ -326,6 +326,41 @@ int us_internal_ssl_socket_is_closed(struct us_internal_ssl_socket_t *s) {
return us_socket_is_closed(0, &s->s);
}
/**
* Override the protocol error if the secure_protocol_method is set. This is to match Node's
* behaviour
* Will modify the verify_error struct to override the error code and reason if necessary.
* Returns 1 if the protocol error was overridden, 0 otherwise
*/
static int should_override_protocol_error(const char *proto, int is_server, int openssl_reason, struct us_bun_verify_error_t *verify_error) {
if (!proto) return 0;
if (is_server) {
if (strcmp(proto, "TLSv1_method") == 0 || strcmp(proto, "TLSv1_1_method") == 0) {
if (openssl_reason == SSL_R_WRONG_VERSION_NUMBER) {
verify_error->code = "ERR_SSL_WRONG_VERSION_NUMBER";
verify_error->reason = "Wrong version number on server";
} else if (openssl_reason == SSL_R_UNSUPPORTED_PROTOCOL) {
verify_error->code = "ERR_SSL_UNSUPPORTED_PROTOCOL";
verify_error->reason = "Unsupported protocol on server";
}
verify_error->error = -1;
ERR_clear_error();
return 1;
}
} else {
if (strcmp(proto, "SSLv23_method") == 0) {
verify_error->code = "ERR_SSL_UNSUPPORTED_PROTOCOL";
verify_error->reason = "Unsupported protocol";
verify_error->error = -1;
ERR_clear_error();
return 1;
}
}
return 0;
}
void us_internal_trigger_handshake_callback_econnreset(struct us_internal_ssl_socket_t *s) {
struct us_internal_ssl_socket_context_t *context =
@@ -348,6 +383,72 @@ void us_internal_trigger_handshake_callback(struct us_internal_ssl_socket_t *s,
if (context->on_handshake != NULL) {
struct us_bun_verify_error_t verify_error = us_internal_verify_error(s);
if (!success && (verify_error.code == NULL || verify_error.code[0] == 0)) {
const char *proto = context->options.secure_protocol_method;
unsigned long err = ERR_peek_error();
int reason = ERR_GET_REASON(err);
if (should_override_protocol_error(proto, SSL_is_server(s->ssl), reason, &verify_error)) {
context->on_handshake(s, success, verify_error, context->handshake_data);
return;
}
if (context->options.secure_protocol_method) {
if (SSL_is_server(s->ssl)) {
unsigned long err = ERR_peek_error();
int reason = ERR_GET_REASON(err);
if ((strcmp(proto, "TLSv1_1_method") == 0 || strcmp(proto, "TLSv1_method") == 0)) {
if (reason == SSL_R_WRONG_VERSION_NUMBER) {
verify_error.code = "ERR_SSL_WRONG_VERSION_NUMBER";
verify_error.reason = "Wrong version number on server";
verify_error.error = -1;
ERR_clear_error();
context->on_handshake(s, success, verify_error, context->handshake_data);
return;
} else if (reason == SSL_R_UNSUPPORTED_PROTOCOL) {
verify_error.code = "ERR_SSL_UNSUPPORTED_PROTOCOL";
verify_error.reason = "Unsupported protocol on server";
verify_error.error = -1;
ERR_clear_error();
context->on_handshake(s, success, verify_error, context->handshake_data);
return;
}
}
} else {
if (strcmp(proto, "SSLv23_method") == 0) {
verify_error.code = "ERR_SSL_UNSUPPORTED_PROTOCOL";
verify_error.reason = "Unsupported protocol";
verify_error.error = -1;
ERR_clear_error();
context->on_handshake(s, success, verify_error, context->handshake_data);
return;
}
}
}
if (verify_error.error == 0) {
verify_error.error = -1;
unsigned long err = ERR_peek_error();
int reason = ERR_GET_REASON(err);
if (reason == SSL_R_TLSV1_ALERT_PROTOCOL_VERSION) {
verify_error.code = "ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION";
verify_error.reason = "TLSv1 alert protocol version";
} else if (reason == SSL_R_UNSUPPORTED_PROTOCOL) {
verify_error.code = "ERR_SSL_UNSUPPORTED_PROTOCOL";
verify_error.reason = SSL_is_server(s->ssl) ? "Unsupported protocol on server" : "Unsupported protocol on client";
} else if (reason == SSL_R_WRONG_VERSION_NUMBER) {
verify_error.code = "ERR_SSL_WRONG_VERSION_NUMBER";
verify_error.reason = "Wrong version number on server";
} else {
verify_error.code = "ERR_SSL_UNSUPPORTED_PROTOCOL";
verify_error.reason = "Unsupported protocol";
}
ERR_clear_error();
}
}
context->on_handshake(s, success, verify_error, context->handshake_data);
}
}
@@ -400,8 +501,7 @@ void us_internal_update_handshake(struct us_internal_ssl_socket_t *s) {
if (us_internal_ssl_socket_is_closed(s) || us_internal_ssl_socket_is_shut_down(s) ||
(s->ssl && SSL_get_shutdown(s->ssl) & SSL_RECEIVED_SHUTDOWN)) {
us_internal_trigger_handshake_callback(s, 0);
us_internal_trigger_handshake_callback_econnreset(s);
return;
}
@@ -507,6 +607,7 @@ restart:
if (just_read <= 0) {
int err = SSL_get_error(s->ssl, just_read);
// as far as I know these are the only errors we want to handle
if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
if (err == SSL_ERROR_WANT_RENEGOTIATE) {
@@ -540,12 +641,11 @@ restart:
}
if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
// clear per thread error queue if it may contain something
ERR_clear_error();
s->fatal_error = 1;
us_internal_trigger_handshake_callback(s, 0);
}
// terminate connection here
// Terminate connection after reporting the handshake error
us_internal_ssl_socket_close(s, 0, NULL);
return NULL; // stop processing data
} else {
@@ -1140,14 +1240,31 @@ SSL_CTX *create_ssl_context_from_bun_options(
/* Create the context */
SSL_CTX *ssl_context = SSL_CTX_new(TLS_method());
/* Default options we rely on - changing these will break our logic */
SSL_CTX_set_read_ahead(ssl_context, 1);
/* we should always accept moving write buffer so we can retry writes with a
* buffer allocated in a different address */
SSL_CTX_set_mode(ssl_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
if (options.min_tls_version > 0) {
if (!SSL_CTX_set_min_proto_version(ssl_context, options.min_tls_version)) {
free_ssl_context(ssl_context);
return NULL;
}
} else {
if (!SSL_CTX_set_min_proto_version(ssl_context, TLS1_2_VERSION)) {
free_ssl_context(ssl_context);
return NULL;
}
}
/* Anything below TLS 1.2 is disabled */
SSL_CTX_set_min_proto_version(ssl_context, TLS1_2_VERSION);
if (options.max_tls_version > 0) {
if (!SSL_CTX_set_max_proto_version(ssl_context, options.max_tls_version)) {
free_ssl_context(ssl_context);
return NULL;
}
}
/* The following are helpers. You may easily implement whatever you want by
* using the native handle directly */
@@ -1545,6 +1662,7 @@ us_internal_bun_create_ssl_socket_context(
context->ssl_context =
ssl_context; // create_ssl_context_from_options(options);
context->is_parent = 1;
context->options = options;
context->on_handshake = NULL;
context->handshake_data = NULL;

View File

@@ -237,6 +237,9 @@ struct us_bun_socket_context_options_t {
int request_cert;
unsigned int client_renegotiation_limit;
unsigned int client_renegotiation_window;
unsigned int min_tls_version;
unsigned int max_tls_version;
const char *secure_protocol_method;
};
/* Return 15-bit timestamp for this context */

View File

@@ -78,6 +78,9 @@ namespace uWS {
int request_cert = 0;
unsigned int client_renegotiation_limit = 3;
unsigned int client_renegotiation_window = 600;
unsigned int min_tls_version = 0;
unsigned int max_tls_version = 0;
const char **secure_protocol_method = nullptr;
/* Conversion operator used internally */
operator struct us_bun_socket_context_options_t() const {

View File

@@ -715,6 +715,12 @@ pub const Listener = struct {
return globalObject.throwValue(err);
};
if (ssl_enabled and create_err != .none) {
const js_err = create_err.toJS(globalObject);
uws.us_socket_context_free(1, socket_context);
return globalObject.throwValue(js_err);
}
if (ssl_enabled) {
if (ssl.?.protos) |p| {
protos = p[0..ssl.?.protos_len];
@@ -1217,6 +1223,12 @@ pub const Listener = struct {
return globalObject.throwValue(err.toErrorInstance(globalObject));
};
if (ssl_enabled and create_err != .none) {
const js_err = create_err.toJS(globalObject);
uws.us_socket_context_free(1, socket_context);
return globalObject.throwValue(js_err);
}
if (ssl_enabled) {
if (ssl.?.protos) |p| {
protos = p[0..ssl.?.protos_len];

View File

@@ -282,6 +282,7 @@ pub fn SSLWrapper(comptime T: type) type {
if (this.flags.closed_notified) return;
this.flags.authorized = success;
// trigger the handshake callback
this.handlers.onHandshake(this.handlers.ctx, success, result);
}
@@ -311,8 +312,25 @@ pub fn SSLWrapper(comptime T: type) type {
if (this.isShutdown()) {
return .{};
}
const ssl = this.ssl orelse return .{};
return uws.us_ssl_socket_verify_error_from_ssl(ssl);
const peek_err = BoringSSL.ERR_peek_error();
var verr = uws.us_ssl_socket_verify_error_from_ssl(ssl);
// no certificate verification = handshake error
if (verr.code == null and peek_err != 0) {
const reason_ptr = BoringSSL.ERR_reason_error_string(peek_err);
verr = uws.us_bun_verify_error_t{
.error_no = @intCast(peek_err),
.code = reason_ptr,
.reason = reason_ptr,
};
BoringSSL.ERR_clear_error();
}
return verr;
}
/// Update the handshake state

View File

@@ -693,6 +693,10 @@ pub const ServerConfig = struct {
client_renegotiation_limit: u32 = 0,
client_renegotiation_window: u32 = 0,
min_version: ?u16 = null,
max_version: ?u16 = null,
secure_protocol_method: ?[*:0]const u8 = null,
const log = Output.scoped(.SSLConfig, false);
pub fn asUSockets(this: SSLConfig) uws.us_bun_socket_context_options_t {
@@ -729,6 +733,18 @@ pub const ServerConfig = struct {
ctx_opts.request_cert = this.request_cert;
ctx_opts.reject_unauthorized = this.reject_unauthorized;
if (this.min_version) |version| {
ctx_opts.min_tls_version = version;
}
if (this.max_version) |version| {
ctx_opts.max_tls_version = version;
}
if (this.secure_protocol_method != null) {
ctx_opts.secure_protocol_method = this.secure_protocol_method;
}
return ctx_opts;
}
@@ -743,6 +759,7 @@ pub const ServerConfig = struct {
"passphrase",
"ssl_ciphers",
"protos",
"secure_protocol_method",
};
inline for (fields) |field| {
@@ -759,7 +776,7 @@ pub const ServerConfig = struct {
{
//numbers
const fields = .{ "secure_options", "request_cert", "reject_unauthorized", "low_memory_mode" };
const fields = .{ "secure_options", "request_cert", "reject_unauthorized", "low_memory_mode", "min_version", "max_version" };
inline for (fields) |field| {
const lhs = @field(thisConfig, field);
@@ -1084,6 +1101,26 @@ pub const ServerConfig = struct {
any = true;
}
if (try obj.getTruthy(global, "minVersion")) |min_version| {
result.min_version = @as(u16, @intCast(min_version.toInt32()));
any = true;
}
if (try obj.getTruthy(global, "maxVersion")) |max_version| {
result.max_version = @as(u16, @intCast(max_version.toInt32()));
any = true;
}
if (try obj.getTruthy(global, "secureProtocol")) |proto| {
var sliced = try proto.toSlice(global, bun.default_allocator);
defer sliced.deinit();
if (sliced.len > 0) {
result.secure_protocol_method = try bun.default_allocator.dupeZ(u8, sliced.slice());
any = true;
result.requires_custom_request_ctx = true;
}
}
if (try obj.getTruthy(global, "ciphers")) |ssl_ciphers| {
var sliced = try ssl_ciphers.toSlice(global, bun.default_allocator);
defer sliced.deinit();

View File

@@ -241,6 +241,7 @@ const errors: ErrorCodeMapping = [
["ERR_TLS_PSK_SET_IDENTITY_HINT_FAILED", Error],
["ERR_TLS_RENEGOTIATION_DISABLED", Error],
["ERR_TLS_SNI_FROM_SERVER", Error],
["ERR_SSL_UNSUPPORTED_PROTOCOL", Error],
["ERR_UNAVAILABLE_DURING_EXIT", Error],
["ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET", Error],
["ERR_UNESCAPED_CHARACTERS", TypeError],

View File

@@ -0,0 +1,20 @@
const std = @import("std");
const bun = @import("bun");
const JSC = bun.JSC;
pub fn getDefaultMinTLSVersionFromCLIFlag(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue {
if (bun.tls.min_tls_version_from_cli_flag) |version| {
return JSC.JSValue.jsNumber(version);
}
return JSC.JSValue.jsNull();
}
pub fn getDefaultMaxTLSVersionFromCLIFlag(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue {
if (bun.tls.max_tls_version_from_cli_flag) |version| {
return JSC.JSValue.jsNumber(version);
}
return JSC.JSValue.jsNull();
}

View File

@@ -599,6 +599,7 @@ pub const Bunfig = @import("./bunfig.zig").Bunfig;
pub const HTTPThread = @import("./http.zig").HTTPThread;
pub const http = @import("./http.zig");
pub const tls = @import("./tls.zig");
pub const Analytics = @import("./analytics/analytics_thread.zig");

View File

@@ -247,6 +247,12 @@ pub const Arguments = struct {
clap.parseParam("--zero-fill-buffers Boolean to force Buffer.allocUnsafe(size) to be zero-filled.") catch unreachable,
clap.parseParam("--redis-preconnect Preconnect to $REDIS_URL at startup") catch unreachable,
clap.parseParam("--no-addons Throw an error if process.dlopen is called, and disable export condition \"node-addons\"") catch unreachable,
clap.parseParam("--tls-max-v1.2 Set the maximum TLS version to 1.2") catch unreachable,
clap.parseParam("--tls-max-v1.3 Set the maximum TLS version to 1.3") catch unreachable,
clap.parseParam("--tls-min-v1.0 Set the minimum TLS version to 1") catch unreachable,
clap.parseParam("--tls-min-v1.1 Set the minimum TLS version to 1.1") catch unreachable,
clap.parseParam("--tls-min-v1.2 Set the minimum TLS version to 1.2") catch unreachable,
clap.parseParam("--tls-min-v1.3 Set the minimum TLS version to 1.3") catch unreachable,
};
const auto_or_run_params = [_]ParamType{
@@ -754,6 +760,27 @@ pub const Arguments = struct {
}
}
// TLS version flags here are specified in ascending order for MAX, and descending order for MIN
// because Node will use the maximum value for --tls-max and the minimum value for --tls-min
// See comments on:
// - https://bun.sh/reference/node/tls/DEFAULT_MAX_VERSION
// - https://bun.sh/reference/node/tls/DEFAULT_MIN_VERSION
// if (args.flag("--tls-max-v1.0")) bun.tls.max_tls_version = 0x0301;
// if (args.flag("--tls-max-v1.1")) bun.tls.max_tls_version = 0x0302;
if (args.flag("--tls-max-v1.2")) bun.tls.max_tls_version_from_cli_flag = 0x0303;
if (args.flag("--tls-max-v1.3")) bun.tls.max_tls_version_from_cli_flag = 0x0304;
if (args.flag("--tls-min-v1.3")) bun.tls.min_tls_version_from_cli_flag = 0x0304;
if (args.flag("--tls-min-v1.2")) bun.tls.min_tls_version_from_cli_flag = 0x0303;
if (args.flag("--tls-min-v1.1")) bun.tls.min_tls_version_from_cli_flag = 0x0302;
if (args.flag("--tls-min-v1.0")) bun.tls.min_tls_version_from_cli_flag = 0x0301;
if (bun.tls.min_tls_version_from_cli_flag != null and bun.tls.max_tls_version_from_cli_flag != null) {
Output.errGeneric("either --tls-min-v1.x or --tls-max-v1.x can be used, not both", .{});
Global.exit(1);
}
ctx.debug.offline_mode_setting = if (args.flag("--prefer-offline"))
Bunfig.OfflineMode.offline
else if (args.flag("--prefer-latest"))

View File

@@ -2552,6 +2552,9 @@ pub const us_bun_socket_context_options_t = extern struct {
request_cert: i32 = 0,
client_renegotiation_limit: u32 = 3,
client_renegotiation_window: u32 = 600,
min_tls_version: u32 = 0,
max_tls_version: u32 = 0,
secure_protocol_method: [*c]const u8 = null,
};
pub const create_bun_socket_error_t = enum(c_int) {
@@ -2581,15 +2584,32 @@ pub const us_bun_verify_error_t = extern struct {
reason: [*c]const u8 = null,
pub fn toJS(this: *const us_bun_verify_error_t, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
const code = if (this.code == null) "" else this.code[0..bun.len(this.code)];
const reason = if (this.reason == null) "" else this.reason[0..bun.len(this.reason)];
const message_slice = if (this.reason != null and bun.len(this.reason) > 0)
this.reason[0..bun.len(this.reason)]
else if (this.code != null and bun.len(this.code) > 0)
this.code[0..bun.len(this.code)]
else
"TLS Error";
const fallback = JSC.SystemError{
.code = bun.String.createUTF8(code),
.message = bun.String.createUTF8(reason),
const code_slice = if (this.code != null and bun.len(this.code) > 0)
this.code[0..bun.len(this.code)]
else
"";
const sys_error_details = JSC.SystemError{
.message = bun.String.createUTF8(message_slice),
.code = bun.String.createUTF8(code_slice),
.errno = this.error_no,
};
return fallback.toErrorInstance(globalObject);
const js_error_value = sys_error_details.toErrorInstance(globalObject);
if (code_slice.len > 0) {
const js_error_obj = js_error_value.toObject(globalObject) catch bun.outOfMemory();
js_error_obj.put(globalObject, JSC.ZigString.static("code"), bun.String.createUTF8ForJS(globalObject, code_slice)) catch bun.outOfMemory();
}
return js_error_value;
}
};
pub extern fn us_ssl_socket_verify_error_from_ssl(ssl: *BoringSSL.SSL) us_bun_verify_error_t;

View File

@@ -682,6 +682,7 @@ declare function $makeAbortError(message?: string, options?: { cause: Error }):
*/
declare function $ERR_INVALID_ARG_TYPE(argName: string, expectedType: string, actualValue: any): TypeError;
declare function $ERR_INVALID_ARG_TYPE(argName: string, expectedTypes: string[], actualValue: any): TypeError;
declare function $ERR_INVALID_ARG_TYPE(message: string): TypeError;
declare function $ERR_INVALID_ARG_VALUE(name: string, value: any, reason?: string): TypeError;
declare function $ERR_UNKNOWN_ENCODING(enc: string): TypeError;
declare function $ERR_STREAM_DESTROYED(method: string): Error;
@@ -695,7 +696,7 @@ declare function $ERR_MISSING_ARGS(...args: [string, ...string[]]): TypeError;
*/
declare function $ERR_MISSING_ARGS(oneOf: string[]): TypeError;
declare function $ERR_INVALID_RETURN_VALUE(expected_type: string, name: string, actual_value: any): TypeError;
declare function $ERR_TLS_INVALID_PROTOCOL_VERSION(a: string, b: string): TypeError;
declare function $ERR_TLS_INVALID_PROTOCOL_VERSION(a: import("tls").SecureVersion, b: "maximum" | "minimum"): TypeError;
declare function $ERR_TLS_PROTOCOL_VERSION_CONFLICT(a: string, b: string): TypeError;
declare function $ERR_INVALID_IP_ADDRESS(ip: any): TypeError;
declare function $ERR_INVALID_ADDRESS_FAMILY(addressType, host, port): RangeError;

View File

@@ -1,5 +1,33 @@
const { isTypedArray, isArrayBuffer } = require("node:util/types");
const getDefaultMinTLSVersionFromCLIFlag = $newZigFunction(
"node_tls_binding.zig",
"getDefaultMinTLSVersionFromCLIFlag",
0,
) as () => number | null;
const getDefaultMaxTLSVersionFromCLIFlag = $newZigFunction(
"node_tls_binding.zig",
"getDefaultMaxTLSVersionFromCLIFlag",
0,
) as () => number | null;
const TLS_VERSION_MAP = {
"TLSv1": 0x0301,
"TLSv1.1": 0x0302,
"TLSv1.2": 0x0303,
"TLSv1.3": 0x0304,
} as const satisfies Record<import("node:tls").SecureVersion, number>;
const TLS_VERSION_REVERSE_MAP: {
[Key in keyof typeof TLS_VERSION_MAP as (typeof TLS_VERSION_MAP)[Key]]: Key;
} = {
0x0301: "TLSv1",
0x0302: "TLSv1.1",
0x0303: "TLSv1.2",
0x0304: "TLSv1.3",
};
function isPemObject(obj: unknown): obj is { pem: unknown } {
return $isObject(obj) && "pem" in obj;
}
@@ -50,4 +78,254 @@ function isValidTLSArray(obj: unknown) {
const VALID_TLS_ERROR_MESSAGE_TYPES = "string or an instance of Buffer, TypedArray, DataView, or BunFile";
export { VALID_TLS_ERROR_MESSAGE_TYPES, isValidTLSArray, isValidTLSItem, throwOnInvalidTLSArray };
function getTlsVersionOrDefault(version: number | null, fallback: import("node:tls").SecureVersion) {
if (!version) return fallback;
const asString = TLS_VERSION_REVERSE_MAP[version];
if (!asString) return fallback;
return asString;
}
const DEFAULT_MIN_VERSION: import("node:tls").SecureVersion = getTlsVersionOrDefault(
getDefaultMinTLSVersionFromCLIFlag(),
"TLSv1.2",
);
const DEFAULT_MAX_VERSION: import("node:tls").SecureVersion = getTlsVersionOrDefault(
getDefaultMaxTLSVersionFromCLIFlag(),
"TLSv1.3",
);
// const forbiddenProtocols = ["SSLv23_method", "TLSv1_1_method", "TLSv1_method"];
function resolveTLSVersions(options: import("node:tls").TLSSocketOptions): [min: number, max: number] {
// if (typeof options?.secureProtocol === "string" && forbiddenProtocols.includes(options.secureProtocol)) {
// throw $ERR_SSL_UNSUPPORTED_PROTOCOL(`Protocol method ${options.secureProtocol} is not supported`);
// }
const secureProtocol = options?.secureProtocol;
const maybeConflictVersion = options.minVersion || options.maxVersion;
if (secureProtocol && maybeConflictVersion) {
throw $ERR_TLS_PROTOCOL_VERSION_CONFLICT(maybeConflictVersion, secureProtocol);
}
let minVersionName: import("node:tls").SecureVersion = DEFAULT_MIN_VERSION;
let maxVersionName: import("node:tls").SecureVersion = DEFAULT_MAX_VERSION;
// Node's C++ logic: https://github.com/nodejs/node/blob/main/src/crypto/crypto_context.cc
if (typeof secureProtocol === "string") {
if (
secureProtocol === "SSLv2_method" ||
secureProtocol === "SSLv2_server_method" ||
secureProtocol === "SSLv2_client_method"
) {
throw $ERR_TLS_INVALID_PROTOCOL_METHOD("SSLv2 methods disabled");
} else if (
secureProtocol === "SSLv3_method" ||
secureProtocol === "SSLv3_server_method" ||
secureProtocol === "SSLv3_client_method"
) {
throw $ERR_TLS_INVALID_PROTOCOL_METHOD("SSLv3 methods disabled");
} else if (secureProtocol === "SSLv23_method") {
minVersionName = DEFAULT_MIN_VERSION;
maxVersionName = DEFAULT_MAX_VERSION;
} else if (secureProtocol === "SSLv23_server_method") {
minVersionName = DEFAULT_MIN_VERSION;
maxVersionName = DEFAULT_MAX_VERSION;
} else if (secureProtocol === "SSLv23_client_method") {
minVersionName = DEFAULT_MIN_VERSION;
maxVersionName = DEFAULT_MAX_VERSION;
// method = TLS_client_method();
} else if (secureProtocol === "TLS_method") {
minVersionName = "TLSv1";
maxVersionName = "TLSv1.3";
} else if (secureProtocol === "TLS_server_method") {
minVersionName = "TLSv1";
maxVersionName = "TLSv1.3";
// method = TLS_server_method();
} else if (secureProtocol === "TLS_client_method") {
minVersionName = "TLSv1";
maxVersionName = "TLSv1.3";
// method = TLS_client_method();
} else if (secureProtocol === "TLSv1_method") {
minVersionName = maxVersionName = "TLSv1";
} else if (secureProtocol === "TLSv1_server_method") {
minVersionName = maxVersionName = "TLSv1";
// method = TLS_server_method();
} else if (secureProtocol === "TLSv1_client_method") {
minVersionName = maxVersionName = "TLSv1";
// method = TLS_client_method();
} else if (secureProtocol === "TLSv1_1_method") {
minVersionName = maxVersionName = "TLSv1.1";
} else if (secureProtocol === "TLSv1_1_server_method") {
minVersionName = maxVersionName = "TLSv1.1";
// method = TLS_server_method();
} else if (secureProtocol === "TLSv1_1_client_method") {
minVersionName = maxVersionName = "TLSv1.1";
// method = TLS_client_method();
} else if (secureProtocol === "TLSv1_2_method") {
minVersionName = maxVersionName = "TLSv1.2";
} else if (secureProtocol === "TLSv1_2_server_method") {
minVersionName = maxVersionName = "TLSv1.2";
// method = TLS_server_method();
} else if (secureProtocol === "TLSv1_2_client_method") {
minVersionName = maxVersionName = "TLSv1.2";
// method = TLS_client_method();
} else if (secureProtocol === "TLSv1_3_method") {
minVersionName = maxVersionName = "TLSv1.3";
} else if (secureProtocol === "TLSv1_3_server_method") {
minVersionName = maxVersionName = "TLSv1.3";
// method = TLS_server_method();
} else if (secureProtocol === "TLSv1_3_client_method") {
minVersionName = maxVersionName = "TLSv1.3";
// method = TLS_client_method();
} else {
throw $ERR_TLS_INVALID_PROTOCOL_METHOD(`Unknown method: ${secureProtocol}`);
}
} else {
minVersionName = options && options.minVersion !== undefined ? options.minVersion : DEFAULT_MIN_VERSION;
maxVersionName = options && options.maxVersion !== undefined ? options.maxVersion : DEFAULT_MAX_VERSION;
}
let minVersion: number;
let maxVersion: number;
if (typeof minVersionName === "string") {
if (!(minVersionName in TLS_VERSION_MAP)) {
throw $ERR_TLS_INVALID_PROTOCOL_VERSION(minVersionName, "minimum");
}
minVersion = TLS_VERSION_MAP[minVersionName];
} else {
throw $ERR_INVALID_ARG_TYPE("options.minVersion", "string", minVersionName);
}
if (typeof maxVersionName === "string") {
if (!(maxVersionName in TLS_VERSION_MAP)) {
throw $ERR_TLS_INVALID_PROTOCOL_VERSION(maxVersionName, "maximum");
}
maxVersion = TLS_VERSION_MAP[maxVersionName];
} else {
throw $ERR_INVALID_ARG_TYPE("options.maxVersion", "string", maxVersionName);
}
return [minVersion, maxVersion];
}
function validateTLSOptions(options: any) {
if (!options || typeof options !== "object") return;
let cert = options.cert;
if (cert) throwOnInvalidTLSArray("options.cert", cert);
let key = options.key;
if (key) throwOnInvalidTLSArray("options.key", key);
let ca = options.ca;
if (ca) throwOnInvalidTLSArray("options.ca", ca);
if (!$isUndefinedOrNull(options.privateKeyIdentifier)) {
if ($isUndefinedOrNull(options.privateKeyEngine)) {
throw $ERR_INVALID_ARG_VALUE("options.privateKeyEngine", options.privateKeyEngine);
} else if (typeof options.privateKeyEngine !== "string") {
throw $ERR_INVALID_ARG_TYPE(
"options.privateKeyEngine",
["string", "null", "undefined"],
options.privateKeyEngine,
);
}
if (typeof options.privateKeyIdentifier !== "string") {
throw $ERR_INVALID_ARG_TYPE(
"options.privateKeyIdentifier",
["string", "null", "undefined"],
options.privateKeyIdentifier,
);
}
}
const ciphers = options.ciphers;
if (ciphers !== undefined && typeof ciphers !== "string") {
throw $ERR_INVALID_ARG_TYPE("options.ciphers", "string", ciphers);
}
const passphrase = options.passphrase;
if (passphrase !== undefined && typeof passphrase !== "string") {
throw $ERR_INVALID_ARG_TYPE("options.passphrase", "string", passphrase);
}
const servername = options.servername;
if (servername !== undefined && typeof servername !== "string") {
throw $ERR_INVALID_ARG_TYPE("options.servername", "string", servername);
}
const ecdhCurve = options.ecdhCurve;
if (ecdhCurve !== undefined && typeof ecdhCurve !== "string") {
throw $ERR_INVALID_ARG_TYPE("options.ecdhCurve", "string", ecdhCurve);
}
const handshakeTimeout = options.handshakeTimeout;
if (handshakeTimeout !== undefined && typeof handshakeTimeout !== "number") {
throw $ERR_INVALID_ARG_TYPE("options.handshakeTimeout", "number", handshakeTimeout);
}
const sessionTimeout = options.sessionTimeout;
if (sessionTimeout !== undefined && typeof sessionTimeout !== "number") {
throw $ERR_INVALID_ARG_TYPE("options.sessionTimeout", "number", sessionTimeout);
}
const ticketKeys = options.ticketKeys;
if (ticketKeys !== undefined) {
if (!Buffer.isBuffer(ticketKeys)) {
throw $ERR_INVALID_ARG_TYPE("options.ticketKeys", "Buffer", ticketKeys);
}
if (ticketKeys.length !== 48) {
throw $ERR_INVALID_ARG_VALUE(
"options.ticketKeys",
ticketKeys.length,
"The property 'options.ticketKeys' must be exactly 48 bytes",
);
}
}
const secureOptions = options.secureOptions || 0;
if (secureOptions && typeof secureOptions !== "number") {
throw $ERR_INVALID_ARG_TYPE("options.secureOptions", "number", secureOptions);
}
const requestCert = options.requestCert;
if (requestCert !== undefined && typeof requestCert !== "boolean") {
throw $ERR_INVALID_ARG_TYPE("options.requestCert", "boolean", requestCert);
}
const rejectUnauthorized = options.rejectUnauthorized;
if (rejectUnauthorized !== undefined && typeof rejectUnauthorized !== "boolean") {
throw $ERR_INVALID_ARG_TYPE("options.rejectUnauthorized", "boolean", rejectUnauthorized);
}
}
let warnOnAllowUnauthorized = true;
function getAllowUnauthorized(): boolean {
const allowUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED === "0";
if (allowUnauthorized && warnOnAllowUnauthorized) {
warnOnAllowUnauthorized = false;
process.emitWarning(
"Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS " +
"connections and HTTPS requests insecure by disabling certificate verification.",
);
}
return allowUnauthorized;
}
export default {
getAllowUnauthorized,
isValidTLSArray,
isValidTLSItem,
resolveTLSVersions,
throwOnInvalidTLSArray,
VALID_TLS_ERROR_MESSAGE_TYPES,
DEFAULT_MIN_VERSION,
DEFAULT_MAX_VERSION,
validateTLSOptions,
TLS_VERSION_REVERSE_MAP,
TLS_VERSION_MAP,
};

View File

@@ -22,6 +22,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
const { Duplex } = require("node:stream");
const EventEmitter = require("node:events");
const { getAllowUnauthorized } = require("internal/tls");
const [addServerName, upgradeDuplexToTLS, isNamedPipeSocket, getBufferedAmount] = $zig(
"socket.zig",
"createNodeTLSBinding",
@@ -100,6 +101,7 @@ const kclosed = Symbol("closed");
const kended = Symbol("ended");
const kwriteCallback = Symbol("writeCallback");
const kSocketClass = Symbol("kSocketClass");
const kSocketOptions = Symbol("kSocketOptions");
function endNT(socket, callback, err) {
socket.$end();
@@ -272,15 +274,21 @@ const SocketHandlers: SocketHandler = {
self.emit("secure", self);
self.alpnProtocol = socket.alpnProtocol;
const { checkServerIdentity } = self[bunTLSConnectOptions];
if (!verifyError && typeof checkServerIdentity === "function" && self.servername) {
const cert = self.getPeerCertificate(true);
verifyError = checkServerIdentity(self.servername, cert);
}
if (self._requestCert || self._rejectUnauthorized) {
if (verifyError) {
self.authorized = false;
self.authorizationError = verifyError.code || verifyError.message;
if (self._rejectUnauthorized) {
self.emit("error", verifyError);
self.emit("secure", self);
self.emit("_tlsError", verifyError);
self.server.emit("tlsClientError", verifyError, self);
self.destroy(verifyError);
return;
}
@@ -290,7 +298,9 @@ const SocketHandlers: SocketHandler = {
} else {
self.authorized = true;
}
self.emit("secureConnect", verifyError);
if (success) {
self.emit("secureConnect", verifyError);
}
self.removeListener("end", onConnectEnd);
},
timeout(socket) {
@@ -350,8 +360,18 @@ const ServerHandlers: SocketHandler = {
const self = this.data;
socket[kServerSocket] = self._handle;
const options = self[bunSocketServerOptions];
const { pauseOnConnect, connectionListener, [kSocketClass]: SClass, requestCert, rejectUnauthorized } = options;
const _socket = new SClass({});
const {
pauseOnConnect,
connectionListener,
[kSocketClass]: SClass,
[kSocketOptions]: socketOptions = {},
requestCert,
rejectUnauthorized,
} = options;
const _socket = new SClass(socketOptions);
_socket.isServer = true;
_socket.server = self;
_socket._requestCert = requestCert;
@@ -394,48 +414,63 @@ const ServerHandlers: SocketHandler = {
handshake(socket, success, verifyError) {
const { data: self } = socket;
if (!success && verifyError?.code === "ECONNRESET") {
if (self._hadError) return;
const err = new ConnResetException("socket hang up");
self.emit("_tlsError", err);
self.server.emit("tlsClientError", err, self);
self._hadError = true;
// error before handshake on the server side will only be emitted using tlsClientError
self.destroy();
return;
}
if (!success) {
const err = verifyError || $ERR_SSL_UNSUPPORTED_PROTOCOL("TLS handshake failed");
self._hadError = true;
self.emit("_tlsError", err);
self.server.emit("tlsClientError", err, self);
self.destroy();
return;
}
self._securePending = false;
self.secureConnecting = false;
self._secureEstablished = !!success;
self.servername = socket.getServername();
const server = self.server;
self.alpnProtocol = socket.alpnProtocol;
if (self._requestCert || self._rejectUnauthorized) {
if (verifyError) {
self.authorized = false;
self.authorizationError = verifyError.code || verifyError.message;
server.emit("tlsClientError", verifyError, self);
if (self._rejectUnauthorized) {
// if we reject we still need to emit secure
self.emit("secure", self);
self.emit("_tlsError", verifyError);
self.server.emit("tlsClientError", verifyError, self);
self.destroy(verifyError);
return;
}
} else {
self.authorized = true;
}
} else {
self.authorized = true;
}
const connectionListener = server[bunSocketServerOptions]?.connectionListener;
if (typeof connectionListener === "function") {
connectionListener.$call(server, self);
}
server.emit("secureConnection", self);
// after secureConnection event we emmit secure and secureConnect
self.emit("secure", self);
self.emit("secureConnect", verifyError);
if (!server.pauseOnConnect) {
self.resume();
if (success) {
server.emit("secureConnection", self);
self.emit("secure", self);
self.emit("secureConnect", verifyError);
if (!server.pauseOnConnect) {
self.resume();
}
}
},
error(socket, error) {
@@ -735,7 +770,13 @@ Socket.prototype.connect = function connect(...args) {
const bunTLS = this[bunTlsSymbol];
var tls = undefined;
if (typeof bunTLS === "function") {
tls = bunTLS.$call(this, port, host, true);
tls = bunTLS.$call(
this,
port,
host,
true, // isClient
);
// Client always request Cert
this._requestCert = true;
if (tls) {
@@ -743,7 +784,9 @@ Socket.prototype.connect = function connect(...args) {
this._rejectUnauthorized = rejectUnauthorized;
tls.rejectUnauthorized = rejectUnauthorized;
} else {
this._rejectUnauthorized = tls.rejectUnauthorized;
const allowUnauth = getAllowUnauthorized();
this._rejectUnauthorized = !allowUnauth;
tls.rejectUnauthorized = !allowUnauth;
}
tls.requestCert = true;
tls.session = session || tls.session;
@@ -1451,7 +1494,15 @@ Server.prototype.listen = function listen(port, hostname, onListen) {
if (typeof bunTLS === "function") {
[tls, TLSSocketClass] = bunTLS.$call(this, port, hostname, false);
options.servername = tls.serverName;
options.minVersion = tls.minVersion;
options.maxVersion = tls.maxVersion;
options[kSocketClass] = TLSSocketClass;
options[kSocketOptions] = {
minVersion: tls.minVersionName,
maxVersion: tls.maxVersionName,
};
contexts = tls.contexts;
if (!tls.requestCert) {
tls.rejectUnauthorized = false;

View File

@@ -1,10 +1,15 @@
// Hardcoded module "node:tls"
const { isArrayBufferView, isTypedArray } = require("node:util/types");
const net = require("node:net");
const { Duplex } = require("node:stream");
const [addServerName] = $zig("socket.zig", "createNodeTLSBinding");
const { throwNotImplemented } = require("internal/shared");
const { throwOnInvalidTLSArray } = require("internal/tls");
const {
TLS_VERSION_REVERSE_MAP,
resolveTLSVersions,
DEFAULT_MIN_VERSION,
DEFAULT_MAX_VERSION,
validateTLSOptions,
} = require("internal/tls");
const { Server: NetServer, Socket: NetSocket } = net;
@@ -202,62 +207,36 @@ var InternalSecureContext = class SecureContext {
passphrase;
servername;
secureOptions;
ciphers;
secureProtocol: string | undefined;
minVersion: number | undefined;
maxVersion: number | undefined;
constructor(options) {
const context = {};
if (options) {
validateTLSOptions(options);
let cert = options.cert;
if (cert) {
throwOnInvalidTLSArray("options.cert", cert);
this.cert = cert;
}
if (cert) this.cert = cert;
let key = options.key;
if (key) {
throwOnInvalidTLSArray("options.key", key);
this.key = key;
}
if (key) this.key = key;
let ca = options.ca;
if (ca) {
throwOnInvalidTLSArray("options.ca", ca);
this.ca = ca;
}
const ca = options.ca;
if (ca) this.ca = ca;
let passphrase = options.passphrase;
if (passphrase && typeof passphrase !== "string") {
throw new TypeError("passphrase argument must be an string");
}
this.passphrase = passphrase;
this.ciphers = options.ciphers;
this.passphrase = options.passphrase;
this.servername = options.servername;
this.secureOptions = options.secureOptions || 0;
this.secureProtocol = options.secureProtocol;
let servername = options.servername;
if (servername && typeof servername !== "string") {
throw new TypeError("servername argument must be an string");
}
this.servername = servername;
let secureOptions = options.secureOptions || 0;
if (secureOptions && typeof secureOptions !== "number") {
throw new TypeError("secureOptions argument must be an number");
}
this.secureOptions = secureOptions;
if (!$isUndefinedOrNull(options.privateKeyIdentifier)) {
if ($isUndefinedOrNull(options.privateKeyEngine)) {
// prettier-ignore
throw $ERR_INVALID_ARG_VALUE("options.privateKeyEngine", options.privateKeyEngine);
} else if (typeof options.privateKeyEngine !== "string") {
// prettier-ignore
throw $ERR_INVALID_ARG_TYPE("options.privateKeyEngine", ["string", "null", "undefined"], options.privateKeyEngine);
}
if (typeof options.privateKeyIdentifier !== "string") {
// prettier-ignore
throw $ERR_INVALID_ARG_TYPE("options.privateKeyIdentifier", ["string", "null", "undefined"], options.privateKeyIdentifier);
}
}
const [minVersion, maxVersion] = resolveTLSVersions(options);
this.minVersion = minVersion;
this.maxVersion = maxVersion;
}
this.context = context;
@@ -304,6 +283,8 @@ function TLSSocket(socket?, options?) {
this.authorizationError;
this[krenegotiationDisabled] = undefined;
this.encrypted = true;
this.ciphers = undefined;
this.ecdhCurve = undefined;
const isNetSocketOrDuplex = socket instanceof Duplex;
@@ -313,6 +294,12 @@ function TLSSocket(socket?, options?) {
if (typeof options === "object") {
const { ALPNProtocols } = options;
// use `in` check because passing undefined should throw according to node tests
if ("checkServerIdentity" in options && typeof options.checkServerIdentity !== "function") {
throw $ERR_INVALID_ARG_TYPE("options.checkServerIdentity", "function", options.checkServerIdentity);
}
if (ALPNProtocols) {
convertALPNProtocols(ALPNProtocols, this);
}
@@ -481,6 +468,9 @@ TLSSocket.prototype[buntls] = function (port, host) {
session: this[ksession],
rejectUnauthorized: this._rejectUnauthorized,
requestCert: this._requestCert,
minVersionName: TLS_VERSION_REVERSE_MAP[this[ksecureContext].minVersion],
maxVersionName: TLS_VERSION_REVERSE_MAP[this[ksecureContext].maxVersion],
secureProtocol: this[ksecureContext].secureProtocol,
...this[ksecureContext],
};
};
@@ -505,6 +495,10 @@ function Server(options, secureConnectionListener): void {
this.servername = undefined;
this.ALPNProtocols = undefined;
const [minVersion, maxVersion] = resolveTLSVersions(options || {});
this.minVersion = minVersion;
this.maxVersion = maxVersion;
let contexts: Map<string, typeof InternalSecureContext> | null = null;
this.addContext = function (hostname, context) {
@@ -527,6 +521,7 @@ function Server(options, secureConnectionListener): void {
options = options.context;
}
if (options) {
validateTLSOptions(options);
const { ALPNProtocols } = options;
if (ALPNProtocols) {
@@ -534,48 +529,26 @@ function Server(options, secureConnectionListener): void {
}
let cert = options.cert;
if (cert) {
throwOnInvalidTLSArray("options.cert", cert);
this.cert = cert;
}
if (cert) this.cert = cert;
let key = options.key;
if (key) {
throwOnInvalidTLSArray("options.key", key);
this.key = key;
}
if (key) this.key = key;
let ca = options.ca;
if (ca) {
throwOnInvalidTLSArray("options.ca", ca);
this.ca = ca;
}
if (ca) this.ca = ca;
let passphrase = options.passphrase;
if (passphrase && typeof passphrase !== "string") {
throw $ERR_INVALID_ARG_TYPE("options.passphrase", "string", passphrase);
}
this.passphrase = passphrase;
let servername = options.servername;
if (servername && typeof servername !== "string") {
throw $ERR_INVALID_ARG_TYPE("options.servername", "string", servername);
}
this.servername = servername;
let secureOptions = options.secureOptions || 0;
if (secureOptions && typeof secureOptions !== "number") {
throw $ERR_INVALID_ARG_TYPE("options.secureOptions", "number", secureOptions);
}
this.secureOptions = secureOptions;
this.ciphers = options.ciphers;
this.ecdhCurve = options.ecdhCurve;
this.passphrase = options.passphrase;
this.servername = options.servername;
this.secureOptions = options.secureOptions || 0;
this.secureProtocol = options.secureProtocol;
const requestCert = options.requestCert || false;
if (requestCert) this._requestCert = requestCert;
else this._requestCert = undefined;
const rejectUnauthorized = options.rejectUnauthorized;
if (typeof rejectUnauthorized !== "undefined") {
this._rejectUnauthorized = rejectUnauthorized;
} else this._rejectUnauthorized = rejectUnauthorizedDefault;
@@ -586,7 +559,11 @@ function Server(options, secureConnectionListener): void {
throw Error("Not implented in Bun yet");
};
Server.prototype.setTicketKeys = function () {
Server.prototype.setTicketKeys = function (ticketKeys) {
if (!Buffer.isBuffer(ticketKeys) || ticketKeys.length !== 48) {
throw $ERR_INVALID_ARG_TYPE("Session ticket keys must be a 48-byte buffer");
}
throw Error("Not implented in Bun yet");
};
@@ -598,6 +575,11 @@ function Server(options, secureConnectionListener): void {
cert: this.cert,
ca: this.ca,
passphrase: this.passphrase,
minVersion: this.minVersion,
maxVersion: this.maxVersion,
secureProtocol: this.secureProtocol,
minVersionName: TLS_VERSION_REVERSE_MAP[this.minVersion],
maxVersionName: TLS_VERSION_REVERSE_MAP[this.maxVersion],
secureOptions: this.secureOptions,
rejectUnauthorized: this._rejectUnauthorized,
requestCert: isClient ? true : this._requestCert,
@@ -620,12 +602,11 @@ function createServer(options, connectionListener) {
const DEFAULT_ECDH_CURVE = "auto",
// https://github.com/Jarred-Sumner/uSockets/blob/fafc241e8664243fc0c51d69684d5d02b9805134/src/crypto/openssl.c#L519-L523
DEFAULT_CIPHERS =
"DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256",
DEFAULT_MIN_VERSION = "TLSv1.2",
DEFAULT_MAX_VERSION = "TLSv1.3";
"DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256";
function normalizeConnectArgs(listArgs) {
const args = net._normalizeArgs(listArgs);
// Cast to any to use internal _normalizeArgs helper as in Node.js implementation
const args = (net as any)._normalizeArgs(listArgs);
$assert($isObject(args[0]));
// If args[0] was options, then normalize dealt with it.
@@ -648,10 +629,13 @@ function normalizeConnectArgs(listArgs) {
function connect(...args) {
let normal = normalizeConnectArgs(args);
const options = normal[0];
const { ALPNProtocols } = options;
if (ALPNProtocols) {
convertALPNProtocols(ALPNProtocols, options);
}
return new TLSSocket(options).connect(normal);
}
@@ -669,9 +653,11 @@ function convertProtocols(protocols) {
(p, c, i) => {
const len = Buffer.byteLength(c);
if (len > 255) {
throw new RangeError(
const err = new RangeError(
`The byte length of the protocol at index ${i} exceeds the maximum length. It must be <= 255. Received ${len}`,
);
err.code = "ERR_OUT_OF_RANGE";
throw err;
}
lens[i] = len;
return p + 1 + len;
@@ -696,7 +682,9 @@ function convertALPNProtocols(protocols, out) {
out.ALPNProtocols = convertProtocols(protocols);
} else if (isTypedArray(protocols)) {
// Copy new buffer not to be modified by user.
out.ALPNProtocols = Buffer.from(protocols);
out.ALPNProtocols = Buffer.from(
protocols.buffer.slice(protocols.byteOffset, protocols.byteOffset + protocols.byteLength),
);
} else if (isArrayBufferView(protocols)) {
out.ALPNProtocols = Buffer.from(
protocols.buffer.slice(protocols.byteOffset, protocols.byteOffset + protocols.byteLength),

12
src/tls.zig Normal file
View File

@@ -0,0 +1,12 @@
const bun = @import("bun");
// These variables are set by the CLI parser.
// These get read by node_tls_binding.zig for `tls.ts` to consume and use
// as the values for `tls.DEFAULT_MIN_VERSION` and `tls.DEFAULT_MAX_VERSION`
// A null value means no CLI flag was provided, and the *default* defaults
// are defined in src/js/node/tls.ts
pub var min_tls_version_from_cli_flag: ?u16 = null;
pub var max_tls_version_from_cli_flag: ?u16 = null;

View File

@@ -7,9 +7,10 @@
import { gc as bunGC, sleepSync, spawnSync, unsafe, which, write } from "bun";
import { heapStats } from "bun:jsc";
import { fork, ChildProcess } from "child_process";
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { readFile, readlink, writeFile, readdir, rm } from "fs/promises";
import { ChildProcess, fork } from "child_process";
import detectLibc from "detect-libc";
import { readdir, readFile, readlink, rm, writeFile } from "fs/promises";
import fs, { closeSync, openSync, rmSync } from "node:fs";
import os from "node:os";
import { dirname, isAbsolute, join } from "path";

View File

@@ -33,9 +33,7 @@ declare global {
}
}
expectType(Bun.env.BAZ).is<"BAZ">();
expectType(process.env.BAZ).is<"BAZ">();
expectType(import.meta.env.BAZ).is<"BAZ">();
expectType(node_env.BAZ).is<"BAZ">();
expectType(bun_env.BAZ).is<"BAZ">();
expectType(Bun.env.OTHER).is<string | undefined>();
@@ -44,16 +42,14 @@ expectType(import.meta.env.OTHER).is<string | undefined>();
expectType(node_env.OTHER).is<string | undefined>();
expectType(bun_env.OTHER).is<string | undefined>();
function isAllSame<T>(a: T, b: T, c: T, d: T, e: T) {
return a === b && b === c && c === d && d === e;
}
declare function isAllSame<T>(...args: T[]): void;
//prettier-ignore
{
isAllSame <"FOO"> (process.env.FOO, Bun.env.FOO, import.meta.env.FOO, node_env.FOO, bun_env.FOO);
isAllSame <"BAR"> (process.env.BAR, Bun.env.BAR, import.meta.env.BAR, node_env.BAR, bun_env.BAR);
isAllSame <"BAZ"> (process.env.BAZ, Bun.env.BAZ, import.meta.env.BAZ, node_env.BAZ, bun_env.BAZ);
isAllSame <"BAZ"> ( Bun.env.BAZ, import.meta.env.BAZ, bun_env.BAZ); // process.env doesn't extend import.meta.env
isAllSame <string | undefined> (process.env.OTHER, Bun.env.OTHER, import.meta.env.OTHER, node_env.OTHER, bun_env.OTHER);
}

View File

@@ -34,7 +34,7 @@ const words: Record<string, { reason: string; limit?: number; regex?: boolean }>
[String.raw`: [a-zA-Z0-9_\.\*\?\[\]\(\)]+ = undefined,`]: { reason: "Do not default a struct field to undefined", limit: 241, regex: true },
"usingnamespace": { reason: "Zig 0.15 will remove `usingnamespace`" },
"catch unreachable": { reason: "For out-of-memory, prefer 'catch bun.outOfMemory()'", limit: 1849 },
"catch unreachable": { reason: "For out-of-memory, prefer 'catch bun.outOfMemory()'", limit: 1856 },
"std.fs.Dir": { reason: "Prefer bun.sys + bun.FD instead of std.fs", limit: 180 },
"std.fs.cwd": { reason: "Prefer bun.FD.cwd()", limit: 103 },

View File

@@ -105,4 +105,4 @@ exports.connect = function connect(options, callback) {
if (client.conn)
client.conn.end();
}
};
};

View File

@@ -0,0 +1,137 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const tls = require('tls');
assert.throws(
() => tls.createSecureContext({ ciphers: 1 }),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "options.ciphers" property must be of type string.' +
' Received type number (1)'
});
assert.throws(
() => tls.createServer({ ciphers: 1 }),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "options.ciphers" property must be of type string.' +
' Received type number (1)'
});
assert.throws(
() => tls.createSecureContext({ key: 'dummykey', passphrase: 1 }),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /The "options\.passphrase" property must be of type string/
});
assert.throws(
() => tls.createServer({ key: 'dummykey', passphrase: 1 }),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /The "options\.passphrase" property must be of type string/
});
assert.throws(
() => tls.createServer({ ecdhCurve: 1 }),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /The "options\.ecdhCurve" property must be of type string/
});
assert.throws(
() => tls.createServer({ handshakeTimeout: 'abcd' }),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "options.handshakeTimeout" property must be of type number.' +
" Received type string ('abcd')"
}
);
assert.throws(
() => tls.createServer({ sessionTimeout: 'abcd' }),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /The "options\.sessionTimeout" property must be of type number/
});
assert.throws(
() => tls.createServer({ ticketKeys: 'abcd' }),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /The "options\.ticketKeys" property must be an instance of/
});
assert.throws(() => tls.createServer({ ticketKeys: Buffer.alloc(0) }), {
code: 'ERR_INVALID_ARG_VALUE',
message: /The property 'options\.ticketKeys' must be exactly 48 bytes/
});
{
const buffer = Buffer.from('abcd');
const out = {};
tls.convertALPNProtocols(buffer, out);
out.ALPNProtocols.write('efgh');
assert(buffer.equals(Buffer.from('abcd')));
assert(out.ALPNProtocols.equals(Buffer.from('efgh')));
}
{
const arrayBufferViewStr = 'abcd';
const inputBuffer = Buffer.from(arrayBufferViewStr.repeat(8), 'utf8');
for (const expectView of common.getArrayBufferViews(inputBuffer)) {
const out = {};
const expected = Buffer.from(expectView.buffer.slice(),
expectView.byteOffset,
expectView.byteLength);
tls.convertALPNProtocols(expectView, out);
assert(out.ALPNProtocols.equals(expected));
}
}
{
const protocols = [(new String('a')).repeat(500)];
const out = {};
assert.throws(
() => tls.convertALPNProtocols(protocols, out),
{
code: 'ERR_OUT_OF_RANGE',
message: 'The byte length of the protocol at index 0 exceeds the ' +
'maximum length. It must be <= 255. Received 500'
}
);
}
assert.throws(() => { tls.createSecureContext({ minVersion: 'fhqwhgads' }); },
{
code: 'ERR_TLS_INVALID_PROTOCOL_VERSION',
name: 'TypeError'
});
assert.throws(() => { tls.createSecureContext({ maxVersion: 'fhqwhgads' }); },
{
code: 'ERR_TLS_INVALID_PROTOCOL_VERSION',
name: 'TypeError'
});
for (const checkServerIdentity of [undefined, null, 1, true]) {
assert.throws(() => {
tls.connect({ checkServerIdentity });
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
});
}

View File

@@ -0,0 +1,15 @@
// Flags: --tls-max-v1.2
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');
// Check that node `--tls-max-v1.2` is supported.
const assert = require('assert');
const tls = require('tls');
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.2');
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.2');
// Check the min-max version protocol versions against these CLI settings.
require('./test-tls-min-max-version.js');

View File

@@ -0,0 +1,15 @@
// Flags: --tls-max-v1.3
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');
// Check that node `--tls-max-v1.3` is supported.
const assert = require('assert');
const tls = require('tls');
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.2');
// Check the min-max version protocol versions against these CLI settings.
require('./test-tls-min-max-version.js');

View File

@@ -0,0 +1,14 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');
// Check that conflicting TLS protocol versions are not allowed
const assert = require('assert');
const child_process = require('child_process');
const args = ['--tls-min-v1.3', '--tls-max-v1.2', '-p', 'process.version'];
child_process.execFile(process.argv[0], args, (err) => {
assert(err);
assert.match(err.message, /not both/);
});

View File

@@ -0,0 +1,15 @@
// Flags: --tls-min-v1.0 --tls-min-v1.1
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');
// Check that `node --tls-v1.0` is supported, and overrides --tls-v1.1.
const assert = require('assert');
const tls = require('tls');
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1');
// Check the min-max version protocol versions against these CLI settings.
require('./test-tls-min-max-version.js');

View File

@@ -0,0 +1,15 @@
// Flags: --tls-min-v1.1
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');
// Check that node `--tls-v1.1` is supported.
const assert = require('assert');
const tls = require('tls');
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.1');
// Check the min-max version protocol versions against these CLI settings.
require('./test-tls-min-max-version.js');

View File

@@ -0,0 +1,15 @@
// Flags: --tls-min-v1.2
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');
// Check that node `--tls-min-v1.2` is supported.
const assert = require('assert');
const tls = require('tls');
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.2');
// Check the min-max version protocol versions against these CLI settings.
require('./test-tls-min-max-version.js');

View File

@@ -0,0 +1,15 @@
// Flags: --tls-min-v1.3
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');
// Check that node `--tls-min-v1.3` is supported.
const assert = require('assert');
const tls = require('tls');
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.3');
// Check the min-max version protocol versions against these CLI settings.
require('./test-tls-min-max-version.js');

View File

@@ -0,0 +1,13 @@
'use strict';
// test-tls-client-reject specifically for TLS1.2.
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const tls = require('tls');
tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
require('./test-tls-client-reject.js');

View File

@@ -0,0 +1,107 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const tls = require('tls');
const fixtures = require('../common/fixtures');
const options = {
key: fixtures.readKey('rsa_private.pem'),
cert: fixtures.readKey('rsa_cert.crt')
};
const server = tls.createServer(options, function(socket) {
socket.pipe(socket);
// Pipe already ends... but leaving this here tests .end() after .end().
socket.on('end', () => socket.end());
}).listen(0, common.mustCall(function() {
unauthorized();
}));
function unauthorized() {
console.log('connect unauthorized');
const socket = tls.connect({
port: server.address().port,
servername: 'localhost',
rejectUnauthorized: false
}, common.mustCall(function() {
let _data;
assert(!socket.authorized);
socket.on('data', common.mustCall((data) => {
assert.strictEqual(data.toString(), 'ok');
_data = data;
}));
socket.on('end', common.mustCall(() => {
assert(_data, 'data failed to echo!');
}));
socket.on('end', () => rejectUnauthorized());
}));
socket.once('session', common.mustCall());
socket.on('error', common.mustNotCall());
socket.end('ok');
}
function rejectUnauthorized() {
console.log('reject unauthorized');
const socket = tls.connect(server.address().port, {
servername: 'localhost'
}, common.mustNotCall());
socket.on('data', common.mustNotCall());
socket.on('error', common.mustCall(function(err) {
rejectUnauthorizedUndefined();
}));
socket.end('ng');
}
function rejectUnauthorizedUndefined() {
console.log('reject unauthorized undefined');
const socket = tls.connect(server.address().port, {
servername: 'localhost',
rejectUnauthorized: undefined
}, common.mustNotCall());
socket.on('data', common.mustNotCall());
socket.on('error', common.mustCall(function(err) {
authorized();
}));
socket.end('ng');
}
function authorized() {
console.log('connect authorized');
const socket = tls.connect(server.address().port, {
ca: [fixtures.readKey('rsa_cert.crt')],
servername: 'localhost'
}, common.mustCall(function() {
console.log('... authorized');
assert(socket.authorized);
socket.on('data', common.mustCall((data) => {
assert.strictEqual(data.toString(), 'ok');
}));
socket.on('end', () => server.close());
}));
socket.on('error', common.mustNotCall());
socket.end('ok');
}

View File

@@ -0,0 +1,283 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto) {
common.skip('missing crypto');
}
const {
hasOpenSSL,
hasOpenSSL3,
} = require('../common/crypto');
const fixtures = require('../common/fixtures');
const { inspect } = require('util');
// Check min/max protocol versions.
const {
assert, connect, keys, tls
} = require(fixtures.path('tls-connect'));
const DEFAULT_MIN_VERSION = tls.DEFAULT_MIN_VERSION;
const DEFAULT_MAX_VERSION = tls.DEFAULT_MAX_VERSION;
function test(cmin, cmax, cprot, smin, smax, sprot, proto, cerr, serr) {
assert(proto || cerr || serr, 'test missing any expectations');
let ciphers;
if (hasOpenSSL3 && (proto === 'TLSv1' || proto === 'TLSv1.1' ||
proto === 'TLSv1_1_method' || proto === 'TLSv1_method' ||
sprot === 'TLSv1_1_method' || sprot === 'TLSv1_method')) {
if (serr !== 'ERR_SSL_UNSUPPORTED_PROTOCOL')
ciphers = 'ALL@SECLEVEL=0';
}
if (hasOpenSSL(3, 1) && cerr === 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION') {
ciphers = 'DEFAULT@SECLEVEL=0';
}
// Report where test was called from. Strip leading garbage from
// at Object.<anonymous> (file:line)
// from the stack location, we only want the file:line part.
const where = inspect(new Error()).split('\n')[2].replace(/[^(]*/, '');
connect({
client: {
checkServerIdentity: (servername, cert) => { },
ca: `${keys.agent1.cert}\n${keys.agent6.ca}`,
minVersion: cmin,
maxVersion: cmax,
secureProtocol: cprot,
ciphers: ciphers,
host: '127.0.0.1', // Required because Bun implements happy eyeballs
},
server: {
cert: keys.agent6.cert,
key: keys.agent6.key,
minVersion: smin,
maxVersion: smax,
secureProtocol: sprot,
ciphers: ciphers,
host: '127.0.0.1', // Required because Bun implements happy eyeballs
},
}, common.mustCall((err, pair, cleanup) => {
function u(_) { return _ === undefined ? 'U' : _; }
console.log('test:', u(cmin), u(cmax), u(cprot), u(smin), u(smax), u(sprot),
u(ciphers), 'expect', u(proto), u(cerr), u(serr));
console.log(' ', where);
if (!proto) {
console.log('client', pair.client.err ? pair.client.err.code : undefined);
console.log('server', pair.server.err ? pair.server.err.code : undefined);
if (cerr) {
assert(pair.client.err);
// Accept these codes as aliases, the one reported depends on the
// OpenSSL version.
if (cerr === 'ERR_SSL_UNSUPPORTED_PROTOCOL' &&
pair.client.err.code === 'ERR_SSL_VERSION_TOO_LOW')
cerr = 'ERR_SSL_VERSION_TOO_LOW';
assert.strictEqual(pair.client.err.code, cerr);
}
if (serr) {
assert(pair.server.err);
assert.strictEqual(pair.server.err.code, serr);
}
return cleanup();
}
assert.ifError(err);
assert.ifError(pair.server.err);
assert.ifError(pair.client.err);
assert(pair.server.conn);
assert(pair.client.conn);
assert.strictEqual(pair.client.conn.getProtocol(), proto);
assert.strictEqual(pair.server.conn.getProtocol(), proto);
return cleanup();
}));
}
const U = undefined;
// Default protocol is the max version.
test(U, U, U, U, U, U, DEFAULT_MAX_VERSION);
// Insecure or invalid protocols cannot be enabled.
test(U, U, U, U, U, 'SSLv2_method',
U, U, 'ERR_TLS_INVALID_PROTOCOL_METHOD');
test(U, U, U, U, U, 'SSLv3_method',
U, U, 'ERR_TLS_INVALID_PROTOCOL_METHOD');
test(U, U, 'SSLv2_method', U, U, U,
U, 'ERR_TLS_INVALID_PROTOCOL_METHOD');
test(U, U, 'SSLv3_method', U, U, U,
U, 'ERR_TLS_INVALID_PROTOCOL_METHOD');
test(U, U, 'hokey-pokey', U, U, U,
U, 'ERR_TLS_INVALID_PROTOCOL_METHOD');
test(U, U, U, U, U, 'hokey-pokey',
U, U, 'ERR_TLS_INVALID_PROTOCOL_METHOD');
// Regression test: this should not crash because node should not pass the error
// message (including unsanitized user input) to a printf-like function.
test(U, U, U, U, U, '%s_method',
U, U, 'ERR_TLS_INVALID_PROTOCOL_METHOD');
// Cannot use secureProtocol and min/max versions simultaneously.
test(U, U, U, U, 'TLSv1.2', 'TLS1_2_method',
U, U, 'ERR_TLS_PROTOCOL_VERSION_CONFLICT');
test(U, U, U, 'TLSv1.2', U, 'TLS1_2_method',
U, U, 'ERR_TLS_PROTOCOL_VERSION_CONFLICT');
test(U, 'TLSv1.2', 'TLS1_2_method', U, U, U,
U, 'ERR_TLS_PROTOCOL_VERSION_CONFLICT');
test('TLSv1.2', U, 'TLS1_2_method', U, U, U,
U, 'ERR_TLS_PROTOCOL_VERSION_CONFLICT');
// TLS_method means "any supported protocol".
test(U, U, 'TLSv1_2_method', U, U, 'TLS_method', 'TLSv1.2');
test(U, U, 'TLSv1_1_method', U, U, 'TLS_method', 'TLSv1.1');
test(U, U, 'TLSv1_method', U, U, 'TLS_method', 'TLSv1');
test(U, U, 'TLS_method', U, U, 'TLSv1_2_method', 'TLSv1.2');
test(U, U, 'TLS_method', U, U, 'TLSv1_1_method', 'TLSv1.1');
test(U, U, 'TLS_method', U, U, 'TLSv1_method', 'TLSv1');
// OpenSSL 1.1.1 and 3.0 use a different error code and alert (sent to the
// client) when no protocols are enabled on the server.
const NO_PROTOCOLS_AVAILABLE_SERVER = hasOpenSSL3 ?
'ERR_SSL_NO_PROTOCOLS_AVAILABLE' : 'ERR_SSL_INTERNAL_ERROR';
const NO_PROTOCOLS_AVAILABLE_SERVER_ALERT = hasOpenSSL3 ?
'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION' : 'ERR_SSL_TLSV1_ALERT_INTERNAL_ERROR';
// SSLv23 also means "any supported protocol" greater than the default
// minimum (which is configurable via command line).
if (DEFAULT_MIN_VERSION === 'TLSv1.3') {
test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method',
U, NO_PROTOCOLS_AVAILABLE_SERVER_ALERT, NO_PROTOCOLS_AVAILABLE_SERVER);
} else {
test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method', 'TLSv1.2');
}
if (DEFAULT_MIN_VERSION === 'TLSv1.3') {
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method',
U, NO_PROTOCOLS_AVAILABLE_SERVER_ALERT, NO_PROTOCOLS_AVAILABLE_SERVER);
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method',
U, NO_PROTOCOLS_AVAILABLE_SERVER_ALERT, NO_PROTOCOLS_AVAILABLE_SERVER);
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method',
U, 'ERR_SSL_NO_PROTOCOLS_AVAILABLE', 'ERR_SSL_UNEXPECTED_MESSAGE');
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method',
U, 'ERR_SSL_NO_PROTOCOLS_AVAILABLE', 'ERR_SSL_UNEXPECTED_MESSAGE');
}
if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method',
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method',
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
// test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method',
// U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
// test(U, U, 'SSLv23_method', U, U, 'TLSv1_method',
// U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
}
if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', 'TLSv1.1');
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method',
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method', 'TLSv1.1');
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method',
U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
}
if (DEFAULT_MIN_VERSION === 'TLSv1') {
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', 'TLSv1.1');
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method', 'TLSv1');
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method', 'TLSv1.1');
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method', 'TLSv1');
}
// TLSv1 thru TLSv1.2 are only supported with explicit configuration with API or
// CLI (--tls-v1.0 and --tls-v1.1).
test(U, U, 'TLSv1_2_method', U, U, 'TLSv1_2_method', 'TLSv1.2');
test(U, U, 'TLSv1_1_method', U, U, 'TLSv1_1_method', 'TLSv1.1');
test(U, U, 'TLSv1_method', U, U, 'TLSv1_method', 'TLSv1');
// The default default.
if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
test(U, U, 'TLSv1_1_method', U, U, U,
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
test(U, U, 'TLSv1_method', U, U, U,
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
if (DEFAULT_MAX_VERSION === 'TLSv1.2') {
test(U, U, U, U, U, 'TLSv1_1_method',
U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
test(U, U, U, U, U, 'TLSv1_method',
U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
} else {
// TLS1.3 client hellos are are not understood by TLS1.1 or below.
test(U, U, U, U, U, 'TLSv1_1_method',
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
test(U, U, U, U, U, 'TLSv1_method',
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
}
}
// The default with --tls-v1.1.
if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
test(U, U, 'TLSv1_1_method', U, U, U, 'TLSv1.1');
test(U, U, 'TLSv1_method', U, U, U,
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
test(U, U, U, U, U, 'TLSv1_1_method', 'TLSv1.1');
if (DEFAULT_MAX_VERSION === 'TLSv1.2') {
test(U, U, U, U, U, 'TLSv1_method',
U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
} else {
// TLS1.3 client hellos are are not understood by TLS1.1 or below.
test(U, U, U, U, U, 'TLSv1_method',
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
}
}
// The default with --tls-v1.0.
if (DEFAULT_MIN_VERSION === 'TLSv1') {
test(U, U, 'TLSv1_1_method', U, U, U, 'TLSv1.1');
test(U, U, 'TLSv1_method', U, U, U, 'TLSv1');
test(U, U, U, U, U, 'TLSv1_1_method', 'TLSv1.1');
test(U, U, U, U, U, 'TLSv1_method', 'TLSv1');
}
// TLS min/max are respected when set with no secureProtocol.
test('TLSv1', 'TLSv1.2', U, U, U, 'TLSv1_method', 'TLSv1');
test('TLSv1', 'TLSv1.2', U, U, U, 'TLSv1_1_method', 'TLSv1.1');
test('TLSv1', 'TLSv1.2', U, U, U, 'TLSv1_2_method', 'TLSv1.2');
test('TLSv1', 'TLSv1.2', U, U, U, 'TLS_method', 'TLSv1.2');
test(U, U, 'TLSv1_method', 'TLSv1', 'TLSv1.2', U, 'TLSv1');
test(U, U, 'TLSv1_1_method', 'TLSv1', 'TLSv1.2', U, 'TLSv1.1');
test(U, U, 'TLSv1_2_method', 'TLSv1', 'TLSv1.2', U, 'TLSv1.2');
test('TLSv1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.1');
test('TLSv1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.2', U, 'TLSv1.1');
test('TLSv1', 'TLSv1.2', U, 'TLSv1', 'TLSv1.1', U, 'TLSv1.1');
test('TLSv1', 'TLSv1.3', U, 'TLSv1', 'TLSv1.1', U, 'TLSv1.1');
test('TLSv1', 'TLSv1', U, 'TLSv1', 'TLSv1.1', U, 'TLSv1');
test('TLSv1', 'TLSv1.2', U, 'TLSv1', 'TLSv1', U, 'TLSv1');
test('TLSv1', 'TLSv1.3', U, 'TLSv1', 'TLSv1', U, 'TLSv1');
test('TLSv1.1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.2', U, 'TLSv1.1');
test('TLSv1', 'TLSv1.2', U, 'TLSv1.1', 'TLSv1.1', U, 'TLSv1.1');
test('TLSv1', 'TLSv1.2', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.2');
// v-any client can connect to v-specific server
test('TLSv1', 'TLSv1.3', U, 'TLSv1.3', 'TLSv1.3', U, 'TLSv1.3');
test('TLSv1', 'TLSv1.3', U, 'TLSv1.2', 'TLSv1.3', U, 'TLSv1.3');
test('TLSv1', 'TLSv1.3', U, 'TLSv1.2', 'TLSv1.2', U, 'TLSv1.2');
test('TLSv1', 'TLSv1.3', U, 'TLSv1.1', 'TLSv1.1', U, 'TLSv1.1');
test('TLSv1', 'TLSv1.3', U, 'TLSv1', 'TLSv1', U, 'TLSv1');
// v-specific client can connect to v-any server
test('TLSv1.3', 'TLSv1.3', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.3');
test('TLSv1.2', 'TLSv1.2', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.2');
test('TLSv1.1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.1');
test('TLSv1', 'TLSv1', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1');

View File

@@ -0,0 +1,58 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const tls = require('tls');
assert.throws(function() {
tls.createSecureContext({ secureProtocol: 'blargh' });
}, {
code: 'ERR_TLS_INVALID_PROTOCOL_METHOD',
message: 'Unknown method: blargh',
});
const errMessageSSLv2 = /SSLv2 methods disabled/;
assert.throws(function() {
tls.createSecureContext({ secureProtocol: 'SSLv2_method' });
}, errMessageSSLv2);
assert.throws(function() {
tls.createSecureContext({ secureProtocol: 'SSLv2_client_method' });
}, errMessageSSLv2);
assert.throws(function() {
tls.createSecureContext({ secureProtocol: 'SSLv2_server_method' });
}, errMessageSSLv2);
const errMessageSSLv3 = /SSLv3 methods disabled/;
assert.throws(function() {
tls.createSecureContext({ secureProtocol: 'SSLv3_method' });
}, errMessageSSLv3);
assert.throws(function() {
tls.createSecureContext({ secureProtocol: 'SSLv3_client_method' });
}, errMessageSSLv3);
assert.throws(function() {
tls.createSecureContext({ secureProtocol: 'SSLv3_server_method' });
}, errMessageSSLv3);
// Note that SSLv2 and SSLv3 are disallowed but SSLv2_method and friends are
// still accepted. They are OpenSSL's way of saying that all known protocols
// are supported unless explicitly disabled (which we do for SSLv2 and SSLv3.)
tls.createSecureContext({ secureProtocol: 'SSLv23_method' });
tls.createSecureContext({ secureProtocol: 'SSLv23_client_method' });
tls.createSecureContext({ secureProtocol: 'SSLv23_server_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_client_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_server_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_1_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_1_client_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_1_server_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_2_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_2_client_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_2_server_method' });

View File

@@ -0,0 +1,24 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto) {
common.skip('missing crypto');
}
const assert = require('assert');
const tls = require('tls');
const server = new tls.Server();
[null, undefined, 0, 1, 1n, Symbol(), {}, [], true, false, '', () => {}]
.forEach((arg) =>
assert.throws(
() => server.setTicketKeys(arg),
{ code: 'ERR_INVALID_ARG_TYPE' }
));
[new Uint8Array(1), Buffer.from([1]), new DataView(new ArrayBuffer(2))].forEach(
(arg) =>
assert.throws(() => {
server.setTicketKeys(arg);
}, /Session ticket keys must be a 48-byte buffer/)
);

View File

@@ -0,0 +1,5 @@
import { describe, it } from "bun:test";
describe("Bun respects the NODE_TLS_REJECT_UNAUTHORIZED environment variables", () => {
it.todo("should reject unauthorized certificates by default");
});

View File

@@ -0,0 +1,103 @@
import { describe, expect, test } from "bun:test";
import { bunEnv, bunExe } from "harness";
const PRINT_MIN = ["-p", "tls.DEFAULT_MIN_VERSION"];
const PRINT_MAX = ["-p", "tls.DEFAULT_MAX_VERSION"];
const TLS_VERSION_TO_SECUREVERSION: Record<`${number}.${number}`, import("tls").SecureVersion> = {
"1.0": "TLSv1",
"1.1": "TLSv1.1",
"1.2": "TLSv1.2",
"1.3": "TLSv1.3",
};
describe("TLS min/max CLI args", () => {
test.each(["1.0", "1.1", "1.2", "1.3"])("TLSv%s", async version => {
const child = Bun.spawn({
cmd: [bunExe(), `--tls-min-v${version}`, ...PRINT_MIN],
stdio: ["pipe", "pipe", "pipe"],
env: bunEnv,
});
const stdout = await Bun.readableStreamToText(child.stdout);
expect(stdout.trim()).toBe(TLS_VERSION_TO_SECUREVERSION[version]);
});
test.each(["1.2", "1.3"])("TLSv%s", async version => {
const child = Bun.spawn({
cmd: [bunExe(), `--tls-max-v${version}`, ...PRINT_MAX],
stdio: ["pipe", "pipe", "pipe"],
env: bunEnv,
});
const stdout = await Bun.readableStreamToText(child.stdout);
expect(stdout.trim()).toBe(`TLSv${version}`);
});
test("Specifying both min and max should exit with error code 1", async () => {
const child = Bun.spawn({
cmd: [bunExe(), "--tls-min-v1.3", "--tls-max-v1.3"],
stdio: ["pipe", "pipe", "pipe"],
env: bunEnv,
});
const stderr = await Bun.readableStreamToText(child.stderr);
expect(stderr.trim()).toMatch(/not both/);
expect(await child.exited).toBe(1);
});
test("Specifying multiple max flags should use the highest version", async () => {
// Node.js docs:
// If multiple of the options are provided, the highest maximum is used.
const child = Bun.spawn({
cmd: [bunExe(), "--tls-max-v1.3", "--tls-max-v1.2", ...PRINT_MAX],
stdio: ["pipe", "pipe", "pipe"],
env: bunEnv,
});
const stdout = await Bun.readableStreamToText(child.stdout);
expect(stdout.trim()).toBe("TLSv1.3");
});
test("Specifying multiple min flags should use the lowest version", async () => {
// Node.js docs:
// If multiple of the options are provided, the lowest minimum is used.
const child = Bun.spawn({
cmd: [bunExe(), "--tls-min-v1.3", "--tls-min-v1.2", ...PRINT_MIN],
stdio: ["pipe", "pipe", "pipe"],
env: bunEnv,
});
const stdout = await Bun.readableStreamToText(child.stdout);
expect(stdout.trim()).toBe("TLSv1.2");
});
test("invalid min/max vals should do nothing since the flags don't exist in the CLI parser", async () => {
const { DEFAULT_MAX_VERSION, DEFAULT_MIN_VERSION } = await import("tls");
{
const child = Bun.spawn({
cmd: [bunExe(), "--tls-max-v1.9999", ...PRINT_MAX],
env: bunEnv,
});
const stdout = await Bun.readableStreamToText(child.stdout);
expect(stdout.trim()).toBe(DEFAULT_MAX_VERSION);
}
{
const child = Bun.spawn({
cmd: [bunExe(), "--tls-min-v1.9999", ...PRINT_MIN],
env: bunEnv,
});
const stdout = await Bun.readableStreamToText(child.stdout);
expect(stdout.trim()).toBe(DEFAULT_MIN_VERSION);
}
});
});