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 }