-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
security_context.dart
271 lines (244 loc) · 11.6 KB
/
security_context.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of dart.io;
/// The object containing the certificates to trust when making
/// a secure client connection, and the certificate chain and
/// private key to serve from a secure server.
///
/// The [SecureSocket] and [SecureServerSocket] classes take a SecurityContext
/// as an argument to their connect and bind methods.
///
/// Certificates and keys can be added to a SecurityContext from either PEM
/// or PKCS12 containers.
///
/// iOS note: Some methods to add, remove, and inspect certificates are not yet
/// implemented. However, the platform's built-in trusted certificates can
/// be used, by way of [SecurityContext.defaultContext].
abstract interface class SecurityContext {
/// Creates a new [SecurityContext].
///
/// By default, the created [SecurityContext] contains no keys or certificates.
/// These can be added by calling the methods of this class.
///
/// If [withTrustedRoots] is passed as `true`, the [SecurityContext] will be
/// seeded by the trusted root certificates provided as explained below. To
/// obtain a [SecurityContext] containing trusted root certificates,
/// [SecurityContext.defaultContext] is usually sufficient, and should
/// be used instead. However, if the [SecurityContext] containing the trusted
/// root certificates must be modified per-connection, then [withTrustedRoots]
/// should be used.
external factory SecurityContext({bool withTrustedRoots = false});
/// The default security context used by most operation requiring one.
///
/// Secure networking classes with an optional `context` parameter
/// use the [defaultContext] object if the parameter is omitted.
/// This object can also be accessed, and modified, directly.
/// Each isolate has a different [defaultContext] object.
/// The [defaultContext] object uses a list of well-known trusted
/// certificate authorities as its trusted roots. On Linux and Windows, this
/// list is taken from Mozilla, who maintains it as part of Firefox. On,
/// MacOS, iOS, and Android, this list comes from the trusted certificates
/// stores built into the platforms.
external static SecurityContext get defaultContext;
/// Sets the private key for a server certificate or client certificate.
///
/// A secure connection using this SecurityContext will use this key with
/// the server or client certificate to sign and decrypt messages.
/// [file] is the path to a PEM or PKCS12 file containing an encrypted
/// private key, encrypted with [password]. Assuming it is well-formatted, all
/// other contents of [file] are ignored. An unencrypted file can be used,
/// but this is not usual.
///
/// NB: This function calls [File.readAsBytesSync], and will block on file IO.
/// Prefer using [usePrivateKeyBytes].
///
/// iOS note: Only PKCS12 data is supported. It should contain both the private
/// key and the certificate chain. On iOS one call to [usePrivateKey] with this
/// data is used instead of two calls to [useCertificateChain] and
/// [usePrivateKey].
void usePrivateKey(String file, {String? password});
/// Sets the private key for a server certificate or client certificate.
///
/// Like [usePrivateKey], but takes the contents of the file as a list
/// of bytes.
void usePrivateKeyBytes(List<int> keyBytes, {String? password});
/// Add a certificate to the set of trusted X509 certificates
/// used by [SecureSocket] client connections.
///
/// [file] is the path to a PEM or PKCS12 file containing X509 certificates,
/// usually root certificates from certificate authorities. For PKCS12 files,
/// [password] is the password for the file. For PEM files, [password] is
/// ignored. Assuming it is well-formatted, all other contents of [file] are
/// ignored.
///
/// NB: This function calls [File.readAsBytesSync], and will block on file IO.
/// Prefer using [setTrustedCertificatesBytes].
///
/// iOS note: On iOS, this call takes only the bytes for a single DER
/// encoded X509 certificate. It may be called multiple times to add
/// multiple trusted certificates to the context. A DER encoded certificate
/// can be obtained from a PEM encoded certificate by using the openssl tool:
/// ```bash
/// $ openssl x509 -outform der -in cert.pem -out cert.der
/// ```
void setTrustedCertificates(String file, {String? password});
/// Add a certificate to the set of trusted X509 certificates
/// used by [SecureSocket] client connections.
///
/// Like [setTrustedCertificates] but takes the contents of the file.
void setTrustedCertificatesBytes(List<int> certBytes, {String? password});
/// Sets the chain of X509 certificates served by [SecureServerSocket]
/// when making secure connections, including the server certificate.
///
/// [file] is a PEM or PKCS12 file containing X509 certificates, starting with
/// the root authority and intermediate authorities forming the signed
/// chain to the server certificate, and ending with the server certificate.
/// The private key for the server certificate is set by [usePrivateKey]. For
/// PKCS12 files, [password] is the password for the file. For PEM files,
/// [password] is ignored. Assuming it is well-formatted, all
/// other contents of [file] are ignored.
///
/// NB: This function calls [File.readAsBytesSync], and will block on file IO.
/// Prefer using [useCertificateChainBytes].
///
/// iOS note: As noted above, [usePrivateKey] does the job of both
/// that call and this one. On iOS, this call is a no-op.
void useCertificateChain(String file, {String? password});
/// Sets the chain of X509 certificates served by [SecureServerSocket]
/// when making secure connections, including the server certificate.
///
/// Like [useCertificateChain] but takes the contents of the file.
void useCertificateChainBytes(List<int> chainBytes, {String? password});
/// Sets the list of authority names that a [SecureServerSocket] will advertise
/// as accepted when requesting a client certificate from a connecting
/// client.
///
/// The [file] is a PEM or PKCS12 file containing the accepted signing
/// authority certificates - the authority names are extracted from the
/// certificates. For PKCS12 files, [password] is the password for the file.
/// For PEM files, [password] is ignored. Assuming it is well-formatted, all
/// other contents of [file] are ignored.
///
/// NB: This function calls [File.readAsBytesSync], and will block on file IO.
/// Prefer using [setClientAuthoritiesBytes].
///
/// iOS note: This call is not supported.
void setClientAuthorities(String file, {String? password});
/// Sets the list of authority names that a [SecureServerSocket] will advertise
/// as accepted, when requesting a client certificate from a connecting
/// client.
///
/// Like [setClientAuthorities] but takes the contents of the file.
void setClientAuthoritiesBytes(List<int> authCertBytes, {String? password});
/// Whether the platform supports ALPN. This always returns true and will be
/// removed in a future release.
@deprecated
external static bool get alpnSupported;
/// Sets the list of application-level protocols supported by a client
/// connection or server connection. The ALPN (application level protocol
/// negotiation) extension to TLS allows a client to send a list of
/// protocols in the TLS client hello message, and the server to pick
/// one and send the selected one back in its server hello message.
///
/// Separate lists of protocols can be sent for client connections and
/// for server connections, using the same SecurityContext. The [isServer]
/// boolean argument specifies whether to set the list for server connections
/// or client connections.
void setAlpnProtocols(List<String> protocols, bool isServer);
/// If `true`, the [SecurityContext] will allow TLS renegotiation.
/// Renegotiation is only supported as a client and the HelloRequest must be
/// received at a quiet point in the application protocol. This is sufficient
/// to support the legacy use case of requesting a new client certificate
/// between an HTTP request and response in (unpipelined) HTTP/1.1.
/// NOTE: Renegotiation is an extremely problematic protocol feature and
/// should only be used to communicate with legacy servers in environments
/// where it is known to be safe.
abstract bool allowLegacyUnsafeRenegotiation;
/// Encodes a set of supported protocols for ALPN/NPN usage.
///
/// The [protocols] list is expected to contain protocols in descending order
/// of preference.
///
/// See RFC 7301 (https://tools.ietf.org/html/rfc7301) for the encoding of
/// `List<String> protocols`:
/// ```plaintext
/// opaque ProtocolName<1..2^8-1>;
///
/// struct {
/// ProtocolName protocol_name_list<2..2^16-1>
/// } ProtocolNameList;
/// ```
/// The encoding of the opaque `ProtocolName<lower..upper>` vector is
/// described in RFC 2246: 4.3 Vectors.
///
/// Note: Even though this encoding scheme would allow a total
/// `ProtocolNameList` length of 65535, this limit cannot be reached. Testing
/// showed that more than ~ 2^14 bytes will fail to negotiate a protocol.
/// We will be conservative and support only messages up to (1<<13)-1 bytes.
static Uint8List _protocolsToLengthEncoding(List<String>? protocols) {
if (protocols == null || protocols.length == 0) {
return new Uint8List(0);
}
int protocolsLength = protocols.length;
// Calculate the number of bytes we will need if it is ASCII.
int expectedLength = protocolsLength;
for (int i = 0; i < protocolsLength; i++) {
int length = protocols[i].length;
if (length > 0 && length <= 255) {
expectedLength += length;
} else {
throw new ArgumentError(
'Length of protocol must be between 1 and 255 (was: $length).');
}
}
if (expectedLength >= (1 << 13)) {
throw new ArgumentError(
'The maximum message length supported is 2^13-1.');
}
// Try encoding the `List<String> protocols` array using fast ASCII path.
var bytes = new Uint8List(expectedLength);
int bytesOffset = 0;
for (int i = 0; i < protocolsLength; i++) {
String proto = protocols[i];
// Add length byte.
bytes[bytesOffset++] = proto.length;
int bits = 0;
// Add protocol bytes.
for (int j = 0; j < proto.length; j++) {
var char = proto.codeUnitAt(j);
bits |= char;
bytes[bytesOffset++] = char & 0xff;
}
// Go slow case if we have encountered anything non-ascii.
if (bits > 0x7f) {
return _protocolsToLengthEncodingNonAsciiBailout(protocols);
}
}
return bytes;
}
static Uint8List _protocolsToLengthEncodingNonAsciiBailout(
List<String> protocols) {
void addProtocol(List<int> outBytes, String protocol) {
var protocolBytes = utf8.encode(protocol);
var len = protocolBytes.length;
if (len > 255) {
throw new ArgumentError(
'Length of protocol must be between 1 and 255 (was: $len)');
}
// Add length byte.
outBytes.add(len);
// Add protocol bytes.
outBytes.addAll(protocolBytes);
}
List<int> bytes = [];
for (var i = 0; i < protocols.length; i++) {
addProtocol(bytes, protocols[i]);
}
if (bytes.length >= (1 << 13)) {
throw new ArgumentError(
'The maximum message length supported is 2^13-1.');
}
return new Uint8List.fromList(bytes);
}
}