1
1
import type { Config as QuicheConfig } from './native/types' ;
2
2
import { quiche } from './native' ;
3
+ import * as errors from './errors' ;
3
4
4
- // All the algos chrome supports + ed25519
5
- const supportedPrivateKeyAlgosDefault =
6
- 'ed25519:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512' ;
5
+ 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 > ;
7
19
8
- export type TlsConfig =
9
- | {
10
- certChainPem : string | null ;
11
- privKeyPem : string | null ;
12
- }
13
- | {
14
- certChainFromPemFile : string | null ;
15
- privKeyFromPemFile : string | null ;
16
- } ;
20
+ /**
21
+ * Private key as a PEM string or Uint8Array buffer containing PEM formatted
22
+ * key. You can pass multiple keys. The number of keys must match the number
23
+ * of certs. Each key must be associated to the the corresponding cert chain.
24
+ *
25
+ * Currently multiple key and certificate chains is not supported.
26
+ */
27
+ key ?: string | Array < string > | Uint8Array | Array < Uint8Array > ;
17
28
18
- type QUICConfig = {
19
- tlsConfig : TlsConfig | undefined ;
20
- verifyPem : string | undefined ;
21
- verifyFromPemFile : string | undefined ;
22
- supportedPrivateKeyAlgos : string | undefined ;
29
+ /**
30
+ * X.509 certificate chain in PEM format or Uint8Array buffer containing
31
+ * PEM formatted certificate chain. Each string or Uint8Array is a
32
+ * certificate chain in subject to issuer order. Multiple certificate chains
33
+ * can be passed. The number of certificate chains must match the number of
34
+ * keys. Each certificate chain must be associated to the corresponding key.
35
+ *
36
+ * Currently multiple key and certificate chains is not supported.
37
+ */
38
+ cert ?: string | Array < string > | Uint8Array | Array < Uint8Array > ;
39
+
40
+ /**
41
+ * Colon separated list of supported signature algorithms.
42
+ *
43
+ * When this is not set, this defaults to the following list:
44
+ * - rsa_pkcs1_sha256
45
+ * - rsa_pkcs1_sha384
46
+ * - rsa_pkcs1_sha512
47
+ * - rsa_pss_rsae_sha256
48
+ * - rsa_pss_rsae_sha384
49
+ * - rsa_pss_rsae_sha512
50
+ * - ecdsa_secp256r1_sha256
51
+ * - ecdsa_secp384r1_sha384
52
+ * - ecdsa_secp521r1_sha512
53
+ * - ed25519
54
+ */
55
+ sigalgs ?: string ;
56
+
57
+ /**
58
+ * Verify the other peer.
59
+ * Clients by default set this to true.
60
+ * Servers by default set this to false.
61
+ */
23
62
verifyPeer : boolean ;
24
- logKeys : string | undefined ;
63
+
64
+ logKeys ?: string ;
25
65
grease : boolean ;
26
66
maxIdleTimeout : number ;
27
67
maxRecvUdpPayloadSize : number ;
@@ -36,12 +76,28 @@ type QUICConfig = {
36
76
enableEarlyData : boolean ;
37
77
} ;
38
78
79
+ /**
80
+ * BoringSSL does not support:
81
+ * - rsa_pss_pss_sha256
82
+ * - rsa_pss_pss_sha384
83
+ * - rsa_pss_pss_sha512
84
+ * - ed448
85
+ */
86
+ const sigalgs = [
87
+ 'rsa_pkcs1_sha256' ,
88
+ 'rsa_pkcs1_sha384' ,
89
+ 'rsa_pkcs1_sha512' ,
90
+ 'rsa_pss_rsae_sha256' ,
91
+ 'rsa_pss_rsae_sha384' ,
92
+ 'rsa_pss_rsae_sha512' ,
93
+ 'ecdsa_secp256r1_sha256' ,
94
+ 'ecdsa_secp384r1_sha384' ,
95
+ 'ecdsa_secp521r1_sha512' ,
96
+ 'ed25519' ,
97
+ ] . join ( ':' ) ;
98
+
39
99
const clientDefault : QUICConfig = {
40
- tlsConfig : undefined ,
41
- verifyPem : undefined ,
42
- verifyFromPemFile : undefined ,
43
- supportedPrivateKeyAlgos : supportedPrivateKeyAlgosDefault ,
44
- logKeys : undefined ,
100
+ sigalgs,
45
101
verifyPeer : true ,
46
102
grease : true ,
47
103
maxIdleTimeout : 5000 ,
@@ -53,16 +109,13 @@ const clientDefault: QUICConfig = {
53
109
initialMaxStreamsBidi : 100 ,
54
110
initialMaxStreamsUni : 100 ,
55
111
disableActiveMigration : true ,
112
+ // Test if this is needed
56
113
applicationProtos : [ 'http/0.9' ] ,
57
114
enableEarlyData : true ,
58
115
} ;
59
116
60
117
const serverDefault : QUICConfig = {
61
- tlsConfig : undefined ,
62
- verifyPem : undefined ,
63
- verifyFromPemFile : undefined ,
64
- supportedPrivateKeyAlgos : supportedPrivateKeyAlgosDefault ,
65
- logKeys : undefined ,
118
+ sigalgs,
66
119
verifyPeer : false ,
67
120
grease : true ,
68
121
maxIdleTimeout : 5000 ,
@@ -74,40 +127,103 @@ const serverDefault: QUICConfig = {
74
127
initialMaxStreamsBidi : 100 ,
75
128
initialMaxStreamsUni : 100 ,
76
129
disableActiveMigration : true ,
130
+ // Test if this is needed
77
131
applicationProtos : [ 'http/0.9' ] ,
78
132
enableEarlyData : true ,
79
133
} ;
80
134
135
+ const textDecoder = new TextDecoder ( 'utf-8' ) ;
136
+ const textEncoder = new TextEncoder ( ) ;
137
+
81
138
function buildQuicheConfig ( config : QUICConfig ) : QuicheConfig {
82
- let certChainPem : Buffer | null = null ;
83
- let privKeyPem : Buffer | null = null ;
84
- if ( config . tlsConfig != null && 'certChainPem' in config . tlsConfig ) {
85
- if ( config . tlsConfig . certChainPem != null ) {
86
- certChainPem = Buffer . from ( config . tlsConfig . certChainPem ) ;
139
+ if ( config . key != null && config . cert == null ) {
140
+ throw new errors . ErrorQUICConfig (
141
+ 'The cert option must be set when key is set' ,
142
+ ) ;
143
+ } else if ( config . key == null && config . cert != null ) {
144
+ throw new errors . ErrorQUICConfig (
145
+ 'The key option must be set when cert is set' ,
146
+ ) ;
147
+ } else if ( config . key != null && config . cert != null ) {
148
+ if ( Array . isArray ( config . key ) && Array . isArray ( config . cert ) ) {
149
+ if ( config . key . length !== config . cert . length ) {
150
+ throw new errors . ErrorQUICConfig (
151
+ 'The number of keys must match the number of certs' ,
152
+ ) ;
153
+ }
87
154
}
88
- if ( config . tlsConfig . privKeyPem != null ) {
89
- privKeyPem = Buffer . from ( config . tlsConfig . privKeyPem ) ;
155
+ }
156
+ // This is a concatenated CA certificates in PEM format
157
+ let caPEMBuffer : Uint8Array | undefined ;
158
+ if ( config . ca != null ) {
159
+ let caPEMString = '' ;
160
+ if ( typeof config . ca === 'string' ) {
161
+ caPEMString = config . ca . trim ( ) + '\n' ;
162
+ } else if ( config . ca instanceof Uint8Array ) {
163
+ caPEMString = textDecoder . decode ( config . ca ) . trim ( ) + '\n' ;
164
+ } else if ( Array . isArray ( config . ca ) ) {
165
+ for ( const c of config . ca ) {
166
+ if ( typeof c === 'string' ) {
167
+ caPEMString += c . trim ( ) + '\n' ;
168
+ } else {
169
+ caPEMString += textDecoder . decode ( c ) . trim ( ) + '\n' ;
170
+ }
171
+ }
90
172
}
173
+ caPEMBuffer = textEncoder . encode ( caPEMString ) ;
91
174
}
92
- const quicheConfig : QuicheConfig = quiche . Config . withBoringSslCtx (
93
- certChainPem ,
94
- privKeyPem ,
95
- config . supportedPrivateKeyAlgos ?? null ,
96
- config . verifyPem != null ? Buffer . from ( config . verifyPem ) : null ,
97
- config . verifyPeer ,
98
- ) ;
99
- if ( config . tlsConfig != null && 'certChainFromPemFile' in config . tlsConfig ) {
100
- if ( config . tlsConfig ?. certChainFromPemFile != null ) {
101
- quicheConfig . loadCertChainFromPemFile (
102
- config . tlsConfig . certChainFromPemFile ,
103
- ) ;
175
+ // This is an array of private keys in PEM format
176
+ let keyPEMBuffers : Array < Uint8Array > | undefined ;
177
+ if ( config . key != null ) {
178
+ const keyPEMs : Array < string > = [ ] ;
179
+ if ( typeof config . key === 'string' ) {
180
+ keyPEMs . push ( config . key . trim ( ) + '\n' ) ;
181
+ } else if ( config . key instanceof Uint8Array ) {
182
+ keyPEMs . push ( textDecoder . decode ( config . key ) . trim ( ) + '\n' ) ;
183
+ } else if ( Array . isArray ( config . key ) ) {
184
+ for ( const k of config . key ) {
185
+ if ( typeof k === 'string' ) {
186
+ keyPEMs . push ( k . trim ( ) + '\n' ) ;
187
+ } else {
188
+ keyPEMs . push ( textDecoder . decode ( k ) . trim ( ) + '\n' ) ;
189
+ }
190
+ }
104
191
}
105
- if ( config . tlsConfig ?. privKeyFromPemFile != null ) {
106
- quicheConfig . loadPrivKeyFromPemFile ( config . tlsConfig . privKeyFromPemFile ) ;
192
+ keyPEMBuffers = keyPEMs . map ( ( k ) => textEncoder . encode ( k ) ) ;
193
+ }
194
+ // This is an array of certificate chains in PEM format
195
+ let certChainPEMBuffers : Array < Uint8Array > | undefined ;
196
+ if ( config . cert != null ) {
197
+ const certChainPEMs : Array < string > = [ ] ;
198
+ if ( typeof config . cert === 'string' ) {
199
+ certChainPEMs . push ( config . cert . trim ( ) + '\n' ) ;
200
+ } else if ( config . cert instanceof Uint8Array ) {
201
+ certChainPEMs . push ( textDecoder . decode ( config . cert ) . trim ( ) + '\n' ) ;
202
+ } else if ( Array . isArray ( config . cert ) ) {
203
+ for ( const c of config . cert ) {
204
+ if ( typeof c === 'string' ) {
205
+ certChainPEMs . push ( c . trim ( ) + '\n' ) ;
206
+ } else {
207
+ certChainPEMs . push ( textDecoder . decode ( c ) . trim ( ) + '\n' ) ;
208
+ }
209
+ }
107
210
}
211
+ certChainPEMBuffers = certChainPEMs . map ( ( c ) => textEncoder . encode ( c ) ) ;
108
212
}
109
- if ( config . verifyFromPemFile != null ) {
110
- quicheConfig . loadVerifyLocationsFromFile ( config . verifyFromPemFile ) ;
213
+ let quicheConfig : QuicheConfig ;
214
+ try {
215
+ quicheConfig = quiche . Config . withBoringSslCtx (
216
+ config . verifyPeer ,
217
+ caPEMBuffer ,
218
+ keyPEMBuffers ,
219
+ certChainPEMBuffers ,
220
+ config . sigalgs ,
221
+ ) ;
222
+ } catch ( e ) {
223
+ throw new errors . ErrorQUICConfig (
224
+ `Failed to build Quiche config with custom SSL context: ${ e . message } ` ,
225
+ { cause : e }
226
+ ) ;
111
227
}
112
228
if ( config . logKeys != null ) {
113
229
quicheConfig . logKeys ( ) ;
0 commit comments