1 module hunt.net.secure.conscrypt.SSLUtils;
2 
3 // dfmt off
4 version(WITH_HUNT_SECURITY):
5 // dfmt on
6 
7 import hunt.net.secure.conscrypt.NativeCrypto;
8 // import hunt.net.secure.conscrypt.OpenSSLX509Certificate;
9 import hunt.net.Exceptions;
10 
11 // import hunt.security.cert.CertificateFactory;
12 // import hunt.security.cert.X509Certificate;
13 
14 
15 import hunt.collection;
16 import hunt.stream.ByteArrayInputStream;
17 import hunt.logging;
18 import hunt.Exceptions;
19 import hunt.text.Common;
20 
21 import deimos.openssl.ssl3;
22 
23 import std.algorithm;
24 import std.conv;
25 
26 /**
27  * Utility methods for SSL packet processing. Copied from the Netty project.
28  * <p>
29  * This is a public class to allow testing to occur on Android via CTS.
30  */
31 final class SSLUtils {
32     enum bool USE_ENGINE_SOCKET_BY_DEFAULT = true;
33     // Boolean.parseBoolean(
34     //         System.getProperty("org.conscrypt.useEngineSocketByDefault", "false"));
35     private enum int MAX_PROTOCOL_LENGTH = 255;
36 
37     private enum string US_ASCII = ("US-ASCII");
38 
39     // TODO(nathanmittler): Should these be in NativeConstants?
40     enum SessionType {
41         /**
42          * Identifies OpenSSL sessions.
43          */
44         OPEN_SSL = 1,
45 
46         /**
47          * Identifies OpenSSL sessions with OCSP stapled data.
48          */
49         OPEN_SSL_WITH_OCSP = 2,
50 
51         /**
52          * Identifies OpenSSL sessions with TLS SCT data.
53          */
54         OPEN_SSL_WITH_TLS_SCT = 3
55 
56         // SessionType(int value) {
57         //     this.value = value;
58         // }
59 
60         // static bool isSupportedType(int type) {
61         //     return type == OPEN_SSL.value || type == OPEN_SSL_WITH_OCSP.value
62         //             || type == OPEN_SSL_WITH_TLS_SCT.value;
63         // }
64 
65         // int value;
66     }
67 
68     static bool isSupportedType(int type) {
69             return type == cast(int)SessionType.OPEN_SSL || 
70                     type == cast(int)SessionType.OPEN_SSL_WITH_OCSP || 
71                     type == cast(int)SessionType.OPEN_SSL_WITH_TLS_SCT;
72         }
73 
74 
75     /**
76      * This is the maximum overhead when encrypting plaintext as defined by
77      * <a href="https://www.ietf.org/rfc/rfc5246.txt">rfc5264</a>,
78      * <a href="https://www.ietf.org/rfc/rfc5289.txt">rfc5289</a> and openssl implementation itself.
79      *
80      * Please note that we use a padding of 16 here as openssl uses PKC#5 which uses 16 bytes
81      * whilethe spec itself allow up to 255 bytes. 16 bytes is the max for PKC#5 (which handles it
82      * the same way as PKC#7) as we use a block size of 16. See <a
83      * href="https://tools.ietf.org/html/rfc5652#section-6.3">rfc5652#section-6.3</a>.
84      *
85      * 16 (IV) + 48 (MAC) + 1 (Padding_length field) + 15 (Padding) + 1 (ContentType) + 2
86      * (ProtocolVersion) + 2 (Length)
87      *
88      * TODO: We may need to review this calculation once TLS 1.3 becomes available.
89      */
90     private enum int MAX_ENCRYPTION_OVERHEAD_LENGTH = 15 + 48 + 1 + 16 + 1 + 2 + 2;
91 
92     private enum int MAX_ENCRYPTION_OVERHEAD_DIFF = int.max - MAX_ENCRYPTION_OVERHEAD_LENGTH;
93 
94     /** Key type: RSA certificate. */
95     private enum string KEY_TYPE_RSA = "RSA";
96 
97     /** Key type: Elliptic Curve certificate. */
98     private enum string KEY_TYPE_EC = "EC";
99 
100     /**
101      * If the given session is a {@link SessionDecorator}, unwraps the session and returns the
102      * underlying (non-decorated) session. Otherwise, returns the provided session.
103      */
104     // static SSLSession unwrapSession(SSLSession session) {
105     //     while (typeid(session) == typeid(SessionDecorator)) {
106     //         session = (cast(SessionDecorator) session).getDelegate();
107     //     }
108     //     return session;
109     // }
110 
111     // static X509Certificate[] decodeX509CertificateChain(ubyte[][] certChain) {
112     //     int numCerts = cast(int)certChain.length;
113     //     if(numCerts == 0)
114     //         return null;
115     //     tracef("Certificates: %d", numCerts);
116     //     CertificateFactory certificateFactory = getCertificateFactory();
117     //     X509Certificate[] decodedCerts = new X509Certificate[numCerts];
118     //     for (int i = 0; i < numCerts; i++) {
119     //         decodedCerts[i] = decodeX509Certificate(certificateFactory, certChain[i]);
120     //     }
121     //     return decodedCerts;
122     // }
123 
124     // private static CertificateFactory getCertificateFactory() {
125     //     try {
126     //         return CertificateFactory.getInstance("X.509");
127     //     } catch (CertificateException e) {
128     //         return null;
129     //     }
130     // }
131 
132     // private static X509Certificate decodeX509Certificate(CertificateFactory certificateFactory,
133     //         ubyte[] bytes) {
134     //     //tracef("X509Certificate: %(%02X %)", bytes);
135     //     // TODO: Tasks pending completion -@zxp at 8/19/2018, 3:02:24 PM
136     //     // 
137     //     // if (certificateFactory !is null) {
138     //     //     return cast(X509Certificate) certificateFactory.generateCertificate(
139     //     //             new ByteArrayInputStream(cast(byte[])bytes));
140     //     // }
141     //     // return OpenSSLX509Certificate.fromX509Der(bytes);
142     //     implementationMissing(false);
143     //     return null;
144     // }
145 
146     /**
147      * Returns key type constant suitable for calling X509KeyManager.chooseServerAlias or
148      * X509ExtendedKeyManager.chooseEngineServerAlias. Returns {@code null} for key exchanges that
149      * do not use X.509 for server authentication.
150      */
151     static string getServerX509KeyType(long sslCipherNative) {
152         version(Have_hunt_openssl) {
153             // implementationMissing(false);
154             string kx_name = null;
155         }
156         version(Have_boringssl) string kx_name = NativeCrypto.SSL_CIPHER_get_kx_name(sslCipherNative);
157 
158         if (kx_name.equals("RSA") || kx_name.equals("DHE_RSA") || kx_name.equals("ECDHE_RSA")) {
159             return KEY_TYPE_RSA;
160         } else if (kx_name.equals("ECDHE_ECDSA")) {
161             return KEY_TYPE_EC;
162         } else {
163             return null;
164         }
165     }
166 
167     // /**
168     //  * Similar to getServerKeyType, but returns value given TLS
169     //  * ClientCertificateType byte values from a CertificateRequest
170     //  * message for use with X509KeyManager.chooseClientAlias or
171     //  * X509ExtendedKeyManager.chooseEngineClientAlias.
172     //  * <p>
173     //  * Visible for testing.
174     //  */
175     // static string getClientKeyType(byte clientCertificateType) {
176     //     // See also http://www.ietf.org/assignments/tls-parameters/tls-parameters.xml
177     //     switch (clientCertificateType) {
178     //         case NativeConstants.TLS_CT_RSA_SIGN:
179     //             return KEY_TYPE_RSA; // RFC rsa_sign
180     //         case NativeConstants.TLS_CT_ECDSA_SIGN:
181     //             return KEY_TYPE_EC; // RFC ecdsa_sign
182     //         default:
183     //             return null;
184     //     }
185     // }
186 
187     // /**
188     //  * Gets the supported key types for client certificates based on the
189     //  * {@code ClientCertificateType} values provided by the server.
190     //  *
191     //  * @param clientCertificateTypes {@code ClientCertificateType} values provided by the server.
192     //  *        See https://www.ietf.org/assignments/tls-parameters/tls-parameters.xml.
193     //  * @return supported key types that can be used in {@code X509KeyManager.chooseClientAlias} and
194     //  *         {@code X509ExtendedKeyManager.chooseEngineClientAlias}.
195     //  *
196     //  * Visible for testing.
197     //  */
198     // static Set<string> getSupportedClientKeyTypes(byte[] clientCertificateTypes) {
199     //     Set<string> result = new HashSet<string>(clientCertificateTypes.length);
200     //     for (byte keyTypeCode : clientCertificateTypes) {
201     //         string keyType = SSLUtils.getClientKeyType(keyTypeCode);
202     //         if (keyType is null) {
203     //             // Unsupported client key type -- ignore
204     //             continue;
205     //         }
206     //         result.add(keyType);
207     //     }
208     //     return result;
209     // }
210 
211     // static byte[][] encodeSubjectX509Principals(X509Certificate[] certificates)
212     //         throws CertificateEncodingException {
213     //     byte[][] principalBytes = new byte[certificates.length][];
214     //     for (int i = 0; i < certificates.length; i++) {
215     //         principalBytes[i] = certificates[i].getSubjectX500Principal().getEncoded();
216     //     }
217     //     return principalBytes;
218     // }
219 
220     // /**
221     //  * Converts the peer certificates into a cert chain.
222     //  */
223     // static X509Certificate[] toCertificateChain(X509Certificate[] certificates) {
224 
225     //     implementationMissing();
226     //     return null;
227     //     // try {
228     //     //     X509Certificate[] chain =
229     //     //             new X509Certificate[certificates.length];
230 
231     //     //     for (int i = 0; i < certificates.length; i++) {
232     //     //         byte[] encoded = certificates[i].getEncoded();
233     //     //         chain[i] = X509Certificate.getInstance(encoded);
234     //     //     }
235     //     //     return chain;
236     //     // } catch (CertificateEncodingException e) {
237     //     //     SSLPeerUnverifiedException exception = new SSLPeerUnverifiedException(e.getMessage());
238     //     //     exception.initCause(exception);
239     //     //     throw exception;
240     //     // } catch (CertificateException e) {
241     //     //     SSLPeerUnverifiedException exception = new SSLPeerUnverifiedException(e.getMessage());
242     //     //     exception.initCause(exception);
243     //     //     throw exception;
244     //     // }
245     // }
246 
247     /**
248      * Calculates the minimum bytes required in the encrypted output buffer for the given number of
249      * plaintext source bytes.
250      */
251     static int calculateOutNetBufSize(int pendingBytes) {
252         return min(SSL3_RT_MAX_PACKET_SIZE,
253                 MAX_ENCRYPTION_OVERHEAD_LENGTH + min(MAX_ENCRYPTION_OVERHEAD_DIFF, pendingBytes));
254     }
255 
256     /**
257      * Wraps the given exception if it's not already a {@link SSLHandshakeException}.
258      */
259     static SSLHandshakeException toSSLHandshakeException(Throwable e) {
260         if (typeid(e) == typeid(SSLHandshakeException)) {
261             return cast(SSLHandshakeException) e;
262         }
263 
264         return new SSLHandshakeException(e.msg, e);
265     }
266 
267     /**
268      * Wraps the given exception if it's not already a {@link SSLException}.
269      */
270     static SSLException toSSLException(Throwable e) {
271         if (typeid(e) == typeid(SSLException)) {
272             return cast(SSLException) e;
273         }
274         return new SSLException("", e);
275     }
276 
277     static string toProtocolString(byte[] bytes) {
278         if (bytes is null) {
279             return null;
280         }
281         return cast(string)bytes.idup;
282     }
283 
284     static byte[] toProtocolBytes(string protocol) {
285         if (protocol is null) {
286             return null;
287         }
288         return cast(byte[] )protocol.dup;
289     }
290 
291     /**
292      * Decodes the given list of protocols into {@link string}s.
293      * @param protocols the encoded protocol list
294      * @return the decoded protocols or {@link EmptyArray#BYTE} if {@code protocols} is
295      * empty.
296      * @throws NullPointerException if protocols is {@code null}.
297      */
298     static string[] decodeProtocols(ubyte[] protocols) {
299         if (protocols.length == 0) {
300             return null;
301         }
302 
303         int numProtocols = 0;
304         for (size_t i = 0; i < protocols.length;) {
305             int protocolLength = protocols[i];
306             if (protocolLength < 0 || protocolLength > protocols.length - i) {
307                 throw new IllegalArgumentException(
308                     "Protocol has invalid length (" ~  protocolLength.to!string() ~ " at position "
309                         ~  i.to!string() ~ "): " ~  (protocols.length < 50
310                         ? protocols.to!string() : protocols.length.to!string() ~ " byte array"));
311             }
312 
313             numProtocols++;
314             i += 1 + protocolLength;
315         }
316 
317         string[] decoded = new string[numProtocols];
318         for (size_t i = 0, d = 0; i < protocols.length;) {
319             int protocolLength = protocols[i];
320             decoded[d++] = protocolLength > 0
321                     ?  cast(string)protocols[i + 1 .. protocolLength].idup
322                     : "";
323             i += 1 + protocolLength;
324         }
325 
326         return decoded;
327     }
328 
329     /**
330      * Encodes a list of protocols into the wire-format (length-prefixed 8-bit strings).
331      * Requires that all strings be encoded with US-ASCII.
332      *
333      * @param protocols the list of protocols to be encoded
334      * @return the encoded form of the protocol list.
335      * @throws IllegalArgumentException if protocols is {@code null}, or if any element is
336      * {@code null} or an empty string.
337      */
338     static byte[] encodeProtocols(string[] protocols) {
339         if (protocols is null) {
340             throw new IllegalArgumentException("protocols array must be non-null");
341         }
342 
343         if (protocols.length == 0) {
344             return [];
345         }
346 
347         // Calculate the encoded length.
348         int length = 0;
349         for (int i = 0; i < protocols.length; ++i) {
350             string protocol = protocols[i];
351             if (protocol is null) {
352                 throw new IllegalArgumentException("protocol[" ~  i.to!string() ~ "] is null");
353             }
354             int protocolLength = cast(int)protocols[i].length;
355 
356             // Verify that the length is valid here, so that we don't attempt to allocate an array
357             // below if the threshold is violated.
358             if (protocolLength == 0 || protocolLength > MAX_PROTOCOL_LENGTH) {
359                 throw new IllegalArgumentException(
360                     "protocol[" ~  i.to!string() ~ "] has invalid length: " ~  protocolLength.to!string());
361             }
362 
363             // Include a 1-byte prefix for each protocol.
364             length += 1 + protocolLength;
365         }
366 
367         byte[] data = new byte[length];
368         for (int dataIndex = 0, i = 0; i < cast(int)protocols.length; ++i) {
369             string protocol = protocols[i];
370             int protocolLength = cast(int)protocol.length;
371 
372             // Add the length prefix.
373             data[dataIndex++] = cast(byte) protocolLength;
374             for (int ci = 0; ci < protocolLength; ++ci) {
375                 char c = protocol.charAt(ci);
376                 if (c > byte.max) {
377                     // Enforce US-ASCII
378                     throw new IllegalArgumentException("Protocol contains invalid character: "
379                         ~ c.to!string() ~ "(protocol=" ~  protocol ~ ")");
380                 }
381                 data[dataIndex++] = cast(byte) c;
382             }
383         }
384         return data;
385     }
386 
387     /**
388      * Return how much bytes can be read out of the encrypted data. Be aware that this method will
389      * not increase the readerIndex of the given {@link ByteBuffer}.
390      *
391      * @param buffers The {@link ByteBuffer}s to read from. Be aware that they must have at least
392      * {@link org.conscrypt.NativeConstants#SSL3_RT_HEADER_LENGTH} bytes to read, otherwise it will
393      * throw an {@link IllegalArgumentException}.
394      * @return length The length of the encrypted packet that is included in the buffer. This will
395      * return {@code -1} if the given {@link ByteBuffer} is not encrypted at all.
396      * @throws IllegalArgumentException Is thrown if the given {@link ByteBuffer} has not at least
397      * {@link org.conscrypt.NativeConstants#SSL3_RT_HEADER_LENGTH} bytes to read.
398      */
399     static int getEncryptedPacketLength(ByteBuffer[] buffers, int offset) {
400         ByteBuffer buffer = buffers[offset];
401 
402         // Check if everything we need is in one ByteBuffer. If so we can make use of the fast-path.
403         if (buffer.remaining() >= SSL3_RT_HEADER_LENGTH) {
404             return getEncryptedPacketLength(buffer);
405         }
406 
407         // We need to copy 5 bytes into a temporary buffer so we can parse out the packet length
408         // easily.
409         ByteBuffer tmp = BufferUtils.allocate(SSL3_RT_HEADER_LENGTH);
410         do {
411             buffer = buffers[offset++];
412             int pos = buffer.position();
413             int limit = buffer.limit();
414             if (buffer.remaining() > tmp.remaining()) {
415                 buffer.limit(pos + tmp.remaining());
416             }
417             try {
418                 tmp.put(buffer);
419             } finally {
420                 // Restore the original indices.
421                 buffer.limit(limit);
422                 buffer.position(pos);
423             }
424         } while (tmp.hasRemaining());
425 
426         // Done, flip the buffer so we can read from it.
427         tmp.flip();
428         return getEncryptedPacketLength(tmp);
429     }
430 
431     private static int getEncryptedPacketLength(ByteBuffer buffer) {
432         int pos = buffer.position();
433         // SSLv3 or TLS - Check ContentType
434         switch (unsignedByte(buffer.get(pos))) {
435             case SSL3_RT_CHANGE_CIPHER_SPEC:
436             case SSL3_RT_ALERT:
437             case SSL3_RT_HANDSHAKE:
438             case SSL3_RT_APPLICATION_DATA:
439                 break;
440             default:
441                 // SSLv2 or bad data
442                 return -1;
443         }
444 
445         // tracef("%02X %02X %02X %02X", buffer.get(pos + 1), 
446         //     buffer.get(pos + 2), buffer.get(pos + 3), buffer.get(pos + 4));
447 
448         // SSLv3 or TLS - Check ProtocolVersion
449         int majorVersion = unsignedByte(buffer.get(pos + 1));
450         // tracef("majorVersion=%d", majorVersion);
451         if (majorVersion != 3) {
452             // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
453             return -1;
454         }
455 
456         // SSLv3 or TLS
457         int packetLength = unsignedShort(buffer.getShort(pos + 3)) + SSL3_RT_HEADER_LENGTH;
458         // tracef("packetLength=%d", packetLength);
459         if (packetLength <= SSL3_RT_HEADER_LENGTH) {
460             // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
461             return -1;
462         }
463         return packetLength;
464     }
465 
466     private static short unsignedByte(byte b) {
467         return cast(short) (b & 0xFF);
468     }
469 
470     private static int unsignedShort(short s) {
471         return s & 0xFFFF;
472     }
473 
474     private this() {}
475 }
476 
477 
478 /**
479  * States for SSL engines.
480  */
481 final class EngineStates {
482     private this() {}
483 
484     /**
485      * The engine is constructed, but the initial handshake hasn't been started
486      */
487     enum int STATE_NEW = 0;
488 
489     /**
490      * The client/server mode of the engine has been set.
491      */
492     enum int STATE_MODE_SET = 1;
493 
494     /**
495      * The handshake has been started
496      */
497     enum int STATE_HANDSHAKE_STARTED = 2;
498 
499     /**
500      * Listeners of the handshake have been notified of completion but the handshake call
501      * hasn't returned.
502      */
503     enum int STATE_HANDSHAKE_COMPLETED = 3;
504 
505     /**
506      * The handshake call returned but the listeners have not yet been notified. This is expected
507      * behaviour in cut-through mode, where SSL_do_handshake returns before the handshake is
508      * complete. We can now start writing data to the socket.
509      */
510     enum int STATE_READY_HANDSHAKE_CUT_THROUGH = 4;
511 
512     /**
513      * The handshake call has returned and the listeners have been notified. Ready to begin
514      * writing data.
515      */
516     enum int STATE_READY = 5;
517 
518     /**
519      * The inbound direction of the engine has been closed.
520      */
521     enum int STATE_CLOSED_INBOUND = 6;
522 
523     /**
524      * The outbound direction of the engine has been closed.
525      */
526     enum int STATE_CLOSED_OUTBOUND = 7;
527 
528     /**
529      * The engine has been closed.
530      */
531     enum int STATE_CLOSED = 8;
532 }