summaryrefslogtreecommitdiff
path: root/ext/tls/lib.rs
diff options
context:
space:
mode:
authorSean Michael Wykes <sean.wykes@nascent.com.br>2021-08-25 09:25:12 -0300
committerGitHub <noreply@github.com>2021-08-25 14:25:12 +0200
commitdccf4cbe36d66140f9e35a6ee755c3c440d745f9 (patch)
treeaf3114696f1649d77474f69cd3361d58aea34275 /ext/tls/lib.rs
parent5d814a4c244d489b4ae51002a0cf1d3c2fe16058 (diff)
feat(fetch): mTLS client certificates for fetch() (#11721)
This commit adds support for specifying client certificates when using fetch, by means of `Deno.createHttpClient`.
Diffstat (limited to 'ext/tls/lib.rs')
-rw-r--r--ext/tls/lib.rs70
1 files changed, 69 insertions, 1 deletions
diff --git a/ext/tls/lib.rs b/ext/tls/lib.rs
index 8f56f0ffd..7632da5e6 100644
--- a/ext/tls/lib.rs
+++ b/ext/tls/lib.rs
@@ -7,6 +7,7 @@ pub use webpki;
pub use webpki_roots;
use deno_core::error::anyhow;
+use deno_core::error::custom_error;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
@@ -17,9 +18,13 @@ use reqwest::header::USER_AGENT;
use reqwest::redirect::Policy;
use reqwest::Client;
use rustls::internal::msgs::handshake::DigitallySignedStruct;
+use rustls::internal::pemfile::certs;
+use rustls::internal::pemfile::pkcs8_private_keys;
+use rustls::internal::pemfile::rsa_private_keys;
use rustls::Certificate;
use rustls::ClientConfig;
use rustls::HandshakeSignatureValid;
+use rustls::PrivateKey;
use rustls::RootCertStore;
use rustls::ServerCertVerified;
use rustls::ServerCertVerifier;
@@ -28,6 +33,7 @@ use rustls::TLSError;
use rustls::WebPKIVerifier;
use serde::Deserialize;
use std::collections::HashMap;
+use std::io::BufRead;
use std::io::BufReader;
use std::io::Cursor;
use std::sync::Arc;
@@ -156,6 +162,54 @@ pub fn create_client_config(
Ok(tls_config)
}
+pub fn load_certs(
+ reader: &mut dyn BufRead,
+) -> Result<Vec<Certificate>, AnyError> {
+ let certs = certs(reader)
+ .map_err(|_| custom_error("InvalidData", "Unable to decode certificate"))?;
+
+ if certs.is_empty() {
+ let e = custom_error("InvalidData", "No certificates found in cert file");
+ return Err(e);
+ }
+
+ Ok(certs)
+}
+
+fn key_decode_err() -> AnyError {
+ custom_error("InvalidData", "Unable to decode key")
+}
+
+fn key_not_found_err() -> AnyError {
+ custom_error("InvalidData", "No keys found in key file")
+}
+
+/// Starts with -----BEGIN RSA PRIVATE KEY-----
+fn load_rsa_keys(mut bytes: &[u8]) -> Result<Vec<PrivateKey>, AnyError> {
+ let keys = rsa_private_keys(&mut bytes).map_err(|_| key_decode_err())?;
+ Ok(keys)
+}
+
+/// Starts with -----BEGIN PRIVATE KEY-----
+fn load_pkcs8_keys(mut bytes: &[u8]) -> Result<Vec<PrivateKey>, AnyError> {
+ let keys = pkcs8_private_keys(&mut bytes).map_err(|_| key_decode_err())?;
+ Ok(keys)
+}
+
+pub fn load_private_keys(bytes: &[u8]) -> Result<Vec<PrivateKey>, AnyError> {
+ let mut keys = load_rsa_keys(bytes)?;
+
+ if keys.is_empty() {
+ keys = load_pkcs8_keys(bytes)?;
+ }
+
+ if keys.is_empty() {
+ return Err(key_not_found_err());
+ }
+
+ Ok(keys)
+}
+
/// Create new instance of async reqwest::Client. This client supports
/// proxies and doesn't follow redirects.
pub fn create_http_client(
@@ -164,12 +218,26 @@ pub fn create_http_client(
ca_data: Option<Vec<u8>>,
proxy: Option<Proxy>,
unsafely_ignore_certificate_errors: Option<Vec<String>>,
+ client_cert_chain_and_key: Option<(String, String)>,
) -> Result<Client, AnyError> {
- let tls_config = create_client_config(
+ let mut tls_config = create_client_config(
root_cert_store,
ca_data,
unsafely_ignore_certificate_errors,
)?;
+
+ if let Some((cert_chain, private_key)) = client_cert_chain_and_key {
+ // The `remove` is safe because load_private_keys checks that there is at least one key.
+ let private_key = load_private_keys(private_key.as_bytes())?.remove(0);
+
+ tls_config
+ .set_single_client_cert(
+ load_certs(&mut cert_chain.as_bytes())?,
+ private_key,
+ )
+ .expect("invalid client key or certificate");
+ }
+
let mut headers = HeaderMap::new();
headers.insert(USER_AGENT, user_agent.parse().unwrap());
let mut builder = Client::builder()