Skip to content

Commit ec5b8a2

Browse files
committed
WIP
1 parent 1684891 commit ec5b8a2

File tree

3 files changed

+121
-87
lines changed

3 files changed

+121
-87
lines changed

src/config.ts

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ import { quiche } from './native';
33

44

55
type QUICConfig = {
6+
/**
7+
* Certificate authority certificate in PEM format or Uint8Array buffer
8+
* containing PEM formatted certificate. Each string or Uint8Array can be
9+
* one certificate or multiple certificates concatenated together. The order
10+
* does not matter, each is an independent certificate authority. Multiple
11+
* concatenated certificate authorities can be passed. They are all
12+
* concatenated together.
13+
*
14+
* When this is not set, this defaults to the operating system's CA
15+
* certificates. OpenSSL (and forks of OpenSSL) all support the
16+
* environment variables `SSL_CERT_DIR` and `SSL_CERT_FILE`.
17+
*/
18+
ca?: string | Array<string> | Uint8Array | Array<Uint8Array>;
619

720
/**
821
* Private key as a PEM string or Uint8Array buffer containing PEM formatted
@@ -20,20 +33,6 @@ type QUICConfig = {
2033
*/
2134
cert?: string | Array<string> | Uint8Array | Array<Uint8Array>;
2235

23-
/**
24-
* Certificate authority certificate in PEM format or Uint8Array buffer
25-
* containing PEM formatted certificate. Each string or Uint8Array can be
26-
* one certificate or multiple certificates concatenated together. The order
27-
* does not matter, each is an independent certificate authority. Multiple
28-
* concatenated certificate authorities can be passed. They are all
29-
* concatenated together.
30-
*
31-
* When this is not set, this defaults to the operating system's CA
32-
* certificates. OpenSSL (and forks of OpenSSL) all support the
33-
* environment variables `SSL_CERT_DIR` and `SSL_CERT_FILE`.
34-
*/
35-
ca?: string | Array<string> | Uint8Array | Array<Uint8Array>;
36-
3736
/**
3837
* Colon separated list of supported signature algorithms.
3938
*
@@ -130,23 +129,17 @@ const serverDefault: QUICConfig = {
130129
enableEarlyData: true,
131130
};
132131

132+
const textDecoder = new TextDecoder('utf-8');
133+
const textEncoder = new TextEncoder();
134+
133135
function buildQuicheConfig(config: QUICConfig): QuicheConfig {
134-
let certChainPem: Buffer | null = null;
135-
let privKeyPem: Buffer | null = null;
136-
if (config.tlsConfig != null && 'certChainPem' in config.tlsConfig) {
137-
if (config.tlsConfig.certChainPem != null) {
138-
certChainPem = Buffer.from(config.tlsConfig.certChainPem);
139-
}
140-
if (config.tlsConfig.privKeyPem != null) {
141-
privKeyPem = Buffer.from(config.tlsConfig.privKeyPem);
142-
}
143-
}
144136

145-
// Ok let's see
146-
let caBuffer;
137+
// This will be passed in as a Optional<Uint8Array>
138+
139+
// This is a concatenated certificate authority certificates in
140+
// PEM format, separated by `\n`
141+
let caPEMBuffer: Uint8Array | undefined;
147142
if (config.ca != null) {
148-
const textDecoder = new TextDecoder('utf-8');
149-
const textEncoder = new TextEncoder();
150143
let caPEMString = '';
151144
if (typeof config.ca === 'string') {
152145
caPEMString = config.ca.trim() + '\n';
@@ -161,9 +154,29 @@ function buildQuicheConfig(config: QUICConfig): QuicheConfig {
161154
}
162155
}
163156
}
164-
caBuffer = textEncoder.encode(caPEMString);
157+
caPEMBuffer = textEncoder.encode(caPEMString);
165158
}
166159

160+
// Setup: [keyPEM, keyPEM, keyPEM]
161+
// AND [certChainPem, certChainPEM, certChainPEM]
162+
// Then we pass it in
163+
// atm, i don't think this actually works
164+
// due to lack of this `SSL_CTX_add1_chain_cert` function
165+
166+
167+
168+
169+
// let certChainPem: Buffer | null = null;
170+
// let privKeyPem: Buffer | null = null;
171+
// if (config.tlsConfig != null && 'certChainPem' in config.tlsConfig) {
172+
// if (config.tlsConfig.certChainPem != null) {
173+
// certChainPem = Buffer.from(config.tlsConfig.certChainPem);
174+
// }
175+
// if (config.tlsConfig.privKeyPem != null) {
176+
// privKeyPem = Buffer.from(config.tlsConfig.privKeyPem);
177+
// }
178+
// }
179+
167180

168181
const quicheConfig: QuicheConfig = quiche.Config.withBoringSslCtx(
169182
certChainPem,

src/native/napi/config.rs

Lines changed: 60 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// use core::panicking::panic;
21
use napi_derive::napi;
32
use napi::bindgen_prelude::*;
43

@@ -41,97 +40,100 @@ impl Config {
4140
let config = quiche::Config::new(
4241
quiche::PROTOCOL_VERSION
4342
).or_else(
44-
|err| Err(Error::from_reason(err.to_string()))
43+
|err| Err(napi::Error::from_reason(err.to_string()))
4544
)?;
4645
return Ok(Config(config));
4746
}
4847

48+
/// Creates configuration with custom TLS context
49+
/// Servers must be setup with a key and cert
4950
#[napi(factory)]
5051
pub fn with_boring_ssl_ctx(
51-
cert_pem: Option<Uint8Array>,
52-
key_pem: Option<Uint8Array>,
53-
supported_key_algos: Option<String>,
54-
ca_cert_pem: Option<Uint8Array>,
5552
verify_peer: bool,
53+
ca: Option<Uint8Array>,
54+
key: Option<Vec<Uint8Array>>,
55+
cert: Option<Vec<Uint8Array>>,
56+
sigalgs: Option<String>,
5657
) -> Result<Self> {
5758
let mut ssl_ctx_builder = boring::ssl::SslContextBuilder::new(
5859
boring::ssl::SslMethod::tls(),
5960
).or_else(
60-
|err| Err(Error::from_reason(err.to_string()))
61+
|e| Err(napi::Error::from_reason(e.to_string()))
6162
)?;
62-
let verify_value = if verify_peer {boring::ssl::SslVerifyMode::PEER | boring::ssl::SslVerifyMode::FAIL_IF_NO_PEER_CERT }
63-
else { boring::ssl::SslVerifyMode::NONE };
63+
let verify_value = if verify_peer {
64+
boring::ssl::SslVerifyMode::PEER | boring::ssl::SslVerifyMode::FAIL_IF_NO_PEER_CERT
65+
} else {
66+
boring::ssl::SslVerifyMode::NONE
67+
};
6468
ssl_ctx_builder.set_verify(verify_value);
65-
// Processing and adding the cert chain
66-
if let Some(cert_pem) = cert_pem {
67-
let x509_cert_chain = boring::x509::X509::stack_from_pem(
68-
&cert_pem.to_vec()
69-
).or_else(
70-
|err| Err(Error::from_reason(err.to_string()))
71-
)?;
72-
for (i, cert) in x509_cert_chain.iter().enumerate() {
73-
if i == 0 {
74-
ssl_ctx_builder.set_certificate(
75-
cert,
76-
).or_else(
77-
|err| Err(Error::from_reason(err.to_string()))
78-
)?;
79-
} else {
80-
ssl_ctx_builder.add_extra_chain_cert(
81-
cert.clone(),
82-
).or_else(
83-
|err| Err(Error::from_reason(err.to_string()))
84-
)?;
85-
}
86-
}
87-
}
88-
// Processing and adding the private key
89-
if let Some(key_pem) = key_pem {
90-
let private_key = boring::pkey::PKey::private_key_from_pem(&key_pem)
91-
.or_else(
92-
|err| Err(Error::from_reason(err.to_string()))
93-
)?;
94-
ssl_ctx_builder.set_private_key(&private_key)
95-
.or_else(
96-
|err| Err(Error::from_reason(err.to_string()))
97-
)?;
98-
}
99-
// Adding supported private key algorithms
100-
if let Some(supported_key_algos) = supported_key_algos {
101-
ssl_ctx_builder.set_sigalgs_list(&supported_key_algos)
69+
// Setup all CA certificates
70+
if let Some(ca) = ca {
71+
let mut x509_store_builder = boring::x509::store::X509StoreBuilder::new()
10272
.or_else(
103-
|err| Err(Error::from_reason(err.to_string()))
73+
|e| Err(napi::Error::from_reason(e.to_string()))
10474
)?;
105-
}
106-
// Processing CA certificate
107-
if let Some(ca_cert_pem) = ca_cert_pem {
10875
let x509_certs = boring::x509::X509::stack_from_pem(
109-
&ca_cert_pem.to_vec()
76+
&ca.to_vec()
11077
).or_else(
111-
|err| Err(Error::from_reason(err.to_string()))
78+
|e| Err(napi::Error::from_reason(e.to_string()))
11279
)?;
113-
let mut x509_store_builder = boring::x509::store::X509StoreBuilder::new()
114-
.or_else(
115-
|err| Err(Error::from_reason(err.to_string()))
116-
)?;
11780
for x509 in x509_certs.into_iter() {
11881
x509_store_builder.add_cert(x509)
11982
.or_else(
120-
|err| Err(Error::from_reason(err.to_string()))
83+
|e| Err(napi::Error::from_reason(e.to_string()))
12184
)?;
12285
}
12386
let x509_store = x509_store_builder.build();
12487
ssl_ctx_builder.set_verify_cert_store(x509_store)
12588
.or_else(
89+
|e| Err(napi::Error::from_reason(e.to_string()))
90+
)?;
91+
}
92+
// Setup all certificates and keys
93+
// The below may not actually work
94+
// We assume we can just use certificate and add them to it
95+
// However this may not be possible
96+
if let (Some(key), Some(cert)) = (key, cert) {
97+
for (k, c) in key.iter().zip(cert.iter()) {
98+
let private_key = boring::pkey::PKey::private_key_from_pem(&k)
99+
.or_else(
126100
|err| Err(Error::from_reason(err.to_string()))
127101
)?;
102+
ssl_ctx_builder.set_private_key(&private_key).or_else(
103+
|e| Err(napi::Error::from_reason(e.to_string()))
104+
)?;
105+
let x509_cert_chain = boring::x509::X509::stack_from_pem(
106+
&c.to_vec()
107+
).or_else(
108+
|err| Err(napi::Error::from_reason(err.to_string()))
109+
)?;
110+
for (i, cert) in x509_cert_chain.iter().enumerate() {
111+
if i == 0 {
112+
ssl_ctx_builder.set_certificate(cert,).or_else(
113+
|err| Err(Error::from_reason(err.to_string()))
114+
)?;
115+
} else {
116+
ssl_ctx_builder.add_extra_chain_cert(
117+
cert.clone(),
118+
).or_else(
119+
|err| Err(Error::from_reason(err.to_string()))
120+
)?;
121+
}
122+
}
123+
}
124+
}
125+
// Setup supported signature algorithms
126+
if let Some(sigalgs) = sigalgs {
127+
ssl_ctx_builder.set_sigalgs_list(&sigalgs).or_else(
128+
|e| Err(napi::Error::from_reason(e.to_string()))
129+
)?;
128130
}
129131
let ssl_ctx= ssl_ctx_builder.build();
130132
let config = quiche::Config::with_boring_ssl_ctx(
131133
quiche::PROTOCOL_VERSION,
132134
ssl_ctx,
133135
).or_else(
134-
|err| Err(Error::from_reason(err.to_string()))
136+
|e| Err(Error::from_reason(e.to_string()))
135137
)?;
136138
return Ok(Config(config));
137139
}

tests/utils.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type QUICServer from '@/QUICServer';
55
import type QUICStream from '@/QUICStream';
66
import { Crypto } from '@peculiar/webcrypto';
77
import * as x509 from '@peculiar/x509';
8+
import { fc } from '@fast-check/jest';
89

910
/**
1011
* WebCrypto polyfill from @peculiar/webcrypto
@@ -654,6 +655,24 @@ const handleStreamProm = async (stream: QUICStream, streamData: StreamData) => {
654655
}
655656
};
656657

658+
659+
// First thing is that we need to create a private key
660+
// To do this... we need to seed the webcrypto
661+
// But also this doesn't quite work
662+
663+
const publicKeyArb = (privateKey: fc.Arbitrary<Buffer> = privateKeyArb) =>
664+
privateKey.map((privateKey) => publicKeyFromPrivateKeyEd25519(privateKey));
665+
666+
const keyPairArb = (
667+
privateKey: fc.Arbitrary<Buffer> = privateKeyArb,
668+
): fc.Arbitrary<KeyPair> =>
669+
privateKey.chain((privateKey) =>
670+
fc.record({
671+
privateKey: fc.constant(privateKey),
672+
publicKey: publicKeyArb(fc.constant(privateKey)),
673+
}),
674+
);
675+
657676
export {
658677
sleep,
659678
randomBytes,

0 commit comments

Comments
 (0)