1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

feat(ext/node): add rootCertificates to node:tls (#25707)

Closes https://github.com/denoland/deno/issues/25604

Signed-off-by: Satya Rohith <me@satyarohith.com>
Co-authored-by: Satya Rohith <me@satyarohith.com>
This commit is contained in:
Luca Casonato 2024-09-18 21:14:26 +02:00 committed by GitHub
parent 5b14c71daf
commit ab1e391e1d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 113 additions and 3 deletions

14
Cargo.lock generated
View file

@ -1859,6 +1859,7 @@ dependencies = [
"thiserror",
"tokio",
"url",
"webpki-root-certs",
"winapi",
"windows-sys 0.52.0",
"x25519-dalek",
@ -5974,9 +5975,9 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
version = "1.7.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
[[package]]
name = "rustls-tokio-stream"
@ -8115,6 +8116,15 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki-root-certs"
version = "0.26.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d93b773107ba49bc84dd3b241e019c702d886fd5c457defe2ea8b1123a5dcd"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "webpki-roots"
version = "0.26.1"

View file

@ -191,6 +191,7 @@ twox-hash = "=1.6.3"
# Upgrading past 2.4.1 may cause WPT failures
url = { version = "< 2.5.0", features = ["serde", "expose_internals"] }
uuid = { version = "1.3.0", features = ["v4"] }
webpki-root-certs = "0.26.5"
webpki-roots = "0.26"
which = "4.2.5"
yoke = { version = "0.7.4", features = ["derive"] }

View file

@ -94,6 +94,7 @@ stable_deref_trait = "1.2.0"
thiserror.workspace = true
tokio.workspace = true
url.workspace = true
webpki-root-certs.workspace = true
winapi.workspace = true
x25519-dalek = { version = "2.0.0", features = ["static_secrets"] }
x509-parser = "0.15.0"

View file

@ -407,6 +407,7 @@ deno_core::extension!(deno_node,
ops::ipc::op_node_ipc_unref,
ops::process::op_node_process_kill,
ops::process::op_process_abort,
ops::tls::op_get_root_certificates,
],
esm_entry_point = "ext:deno_node/02_init.js",
esm = [

View file

@ -11,6 +11,7 @@ pub mod ipc;
pub mod os;
pub mod process;
pub mod require;
pub mod tls;
pub mod util;
pub mod v8;
pub mod vm;

29
ext/node/ops/tls.rs Normal file
View file

@ -0,0 +1,29 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use base64::Engine;
use deno_core::op2;
use webpki_root_certs;
#[op2]
#[serde]
pub fn op_get_root_certificates() -> Vec<String> {
let certs = webpki_root_certs::TLS_SERVER_ROOT_CERTS
.iter()
.map(|cert| {
let b64 = base64::engine::general_purpose::STANDARD.encode(cert);
let pem_lines = b64
.chars()
.collect::<Vec<char>>()
// Node uses 72 characters per line, so we need to follow node even though
// it's not spec compliant https://datatracker.ietf.org/doc/html/rfc7468#section-2
.chunks(72)
.map(|c| c.iter().collect::<String>())
.collect::<Vec<String>>()
.join("\n");
let pem = format!(
"-----BEGIN CERTIFICATE-----\n{pem_lines}\n-----END CERTIFICATE-----\n",
);
pem
})
.collect::<Vec<String>>();
certs
}

View file

@ -7,6 +7,10 @@
import { notImplemented } from "ext:deno_node/_utils.ts";
import tlsCommon from "node:_tls_common";
import tlsWrap from "node:_tls_wrap";
import { op_get_root_certificates } from "ext:core/ops";
import { primordials } from "ext:core/mod.js";
const { ObjectFreeze } = primordials;
// openssl -> rustls
const cipherMap = {
@ -30,7 +34,58 @@ export function getCiphers() {
return Object.keys(cipherMap).map((name) => name.toLowerCase());
}
export const rootCertificates = undefined;
let lazyRootCertificates: string[] | null = null;
function ensureLazyRootCertificates(target: string[]) {
if (lazyRootCertificates === null) {
lazyRootCertificates = op_get_root_certificates() as string[];
lazyRootCertificates.forEach((v) => target.push(v));
ObjectFreeze(target);
}
}
export const rootCertificates = new Proxy([] as string[], {
// @ts-ignore __proto__ is not in the types
__proto__: null,
get(target, prop) {
ensureLazyRootCertificates(target);
return Reflect.get(target, prop);
},
ownKeys(target) {
ensureLazyRootCertificates(target);
return Reflect.ownKeys(target);
},
has(target, prop) {
ensureLazyRootCertificates(target);
return Reflect.has(target, prop);
},
getOwnPropertyDescriptor(target, prop) {
ensureLazyRootCertificates(target);
return Reflect.getOwnPropertyDescriptor(target, prop);
},
set(target, prop, value) {
ensureLazyRootCertificates(target);
return Reflect.set(target, prop, value);
},
defineProperty(target, prop, descriptor) {
ensureLazyRootCertificates(target);
return Reflect.defineProperty(target, prop, descriptor);
},
deleteProperty(target, prop) {
ensureLazyRootCertificates(target);
return Reflect.deleteProperty(target, prop);
},
isExtensible(target) {
ensureLazyRootCertificates(target);
return Reflect.isExtensible(target);
},
preventExtensions(target) {
ensureLazyRootCertificates(target);
return Reflect.preventExtensions(target);
},
setPrototypeOf() {
return false;
},
});
export const DEFAULT_ECDH_CURVE = "auto";
export const DEFAULT_MAX_VERSION = "TLSv1.3";
export const DEFAULT_MIN_VERSION = "TLSv1.2";

View file

@ -1,9 +1,11 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import {
assert,
assertEquals,
assertInstanceOf,
assertStringIncludes,
assertThrows,
} from "@std/assert";
import { delay } from "@std/async/delay";
import { fromFileUrl, join } from "@std/path";
@ -215,3 +217,13 @@ Deno.test("tls.connect() throws InvalidData when there's error in certificate",
"InvalidData: invalid peer certificate: UnknownIssuer",
);
});
Deno.test("tls.rootCertificates is not empty", () => {
assert(tls.rootCertificates.length > 0);
assert(Object.isFrozen(tls.rootCertificates));
assert(tls.rootCertificates instanceof Array);
assert(tls.rootCertificates.every((cert) => typeof cert === "string"));
assertThrows(() => {
(tls.rootCertificates as string[]).push("new cert");
}, TypeError);
});