1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.conscrypt; 18 19 import static org.conscrypt.NativeConstants.SSL_MODE_CBC_RECORD_SPLITTING; 20 import static org.conscrypt.NativeConstants.SSL_OP_CIPHER_SERVER_PREFERENCE; 21 import static org.conscrypt.NativeConstants.SSL_OP_NO_TICKET; 22 import static org.conscrypt.NativeConstants.SSL_RECEIVED_SHUTDOWN; 23 import static org.conscrypt.NativeConstants.SSL_SENT_SHUTDOWN; 24 import static org.conscrypt.NativeConstants.SSL_VERIFY_FAIL_IF_NO_PEER_CERT; 25 import static org.conscrypt.NativeConstants.SSL_VERIFY_NONE; 26 import static org.conscrypt.NativeConstants.SSL_VERIFY_PEER; 27 28 import java.io.FileDescriptor; 29 import java.io.IOException; 30 import java.io.UnsupportedEncodingException; 31 import java.net.SocketException; 32 import java.security.InvalidKeyException; 33 import java.security.PrivateKey; 34 import java.security.PublicKey; 35 import java.security.cert.CertificateEncodingException; 36 import java.security.cert.CertificateException; 37 import java.security.cert.X509Certificate; 38 import java.util.HashSet; 39 import java.util.Set; 40 import java.util.concurrent.locks.ReadWriteLock; 41 import java.util.concurrent.locks.ReentrantReadWriteLock; 42 import javax.crypto.SecretKey; 43 import javax.net.ssl.SSLException; 44 import javax.net.ssl.SSLHandshakeException; 45 import javax.net.ssl.X509KeyManager; 46 import javax.net.ssl.X509TrustManager; 47 import javax.security.auth.x500.X500Principal; 48 import org.conscrypt.NativeCrypto.SSLHandshakeCallbacks; 49 import org.conscrypt.SSLParametersImpl.AliasChooser; 50 import org.conscrypt.SSLParametersImpl.PSKCallbacks; 51 52 /** 53 * A utility wrapper that abstracts operations on the underlying native SSL instance. 54 */ 55 final class NativeSsl { 56 private final SSLParametersImpl parameters; 57 private final SSLHandshakeCallbacks handshakeCallbacks; 58 private final AliasChooser aliasChooser; 59 private final PSKCallbacks pskCallbacks; 60 private X509Certificate[] localCertificates; 61 private final ReadWriteLock lock = new ReentrantReadWriteLock(); 62 private volatile long ssl; 63 64 private NativeSsl(long ssl, SSLParametersImpl parameters, 65 SSLHandshakeCallbacks handshakeCallbacks, AliasChooser aliasChooser, 66 PSKCallbacks pskCallbacks) { 67 this.ssl = ssl; 68 this.parameters = parameters; 69 this.handshakeCallbacks = handshakeCallbacks; 70 this.aliasChooser = aliasChooser; 71 this.pskCallbacks = pskCallbacks; 72 } 73 74 static NativeSsl newInstance(SSLParametersImpl parameters, 75 SSLHandshakeCallbacks handshakeCallbacks, AliasChooser chooser, 76 PSKCallbacks pskCallbacks) throws SSLException { 77 AbstractSessionContext ctx = parameters.getSessionContext(); 78 long ssl = NativeCrypto.SSL_new(ctx.sslCtxNativePointer, ctx); 79 return new NativeSsl(ssl, parameters, handshakeCallbacks, chooser, pskCallbacks); 80 } 81 82 BioWrapper newBio() { 83 try { 84 return new BioWrapper(); 85 } catch (SSLException e) { 86 throw new RuntimeException(e); 87 } 88 } 89 90 void offerToResumeSession(long sslSessionNativePointer) throws SSLException { 91 NativeCrypto.SSL_set_session(ssl, this, sslSessionNativePointer); 92 } 93 94 byte[] getSessionId() { 95 return NativeCrypto.SSL_session_id(ssl, this); 96 } 97 98 long getTime() { 99 return NativeCrypto.SSL_get_time(ssl, this); 100 } 101 102 long getTimeout() { 103 return NativeCrypto.SSL_get_timeout(ssl, this); 104 } 105 106 void setTimeout(long millis) { 107 NativeCrypto.SSL_set_timeout(ssl, this, millis); 108 } 109 110 String getCipherSuite() { 111 return NativeCrypto.cipherSuiteToJava(NativeCrypto.SSL_get_current_cipher(ssl, this)); 112 } 113 114 X509Certificate[] getPeerCertificates() throws CertificateException { 115 byte[][] encoded = NativeCrypto.SSL_get0_peer_certificates(ssl, this); 116 return encoded == null ? null : SSLUtils.decodeX509CertificateChain(encoded); 117 } 118 119 X509Certificate[] getLocalCertificates() { 120 return localCertificates; 121 } 122 123 byte[] getPeerCertificateOcspData() { 124 return NativeCrypto.SSL_get_ocsp_response(ssl, this); 125 } 126 127 byte[] getTlsUnique() { 128 return NativeCrypto.SSL_get_tls_unique(ssl, this); 129 } 130 131 byte[] getPeerTlsSctData() { 132 return NativeCrypto.SSL_get_signed_cert_timestamp_list(ssl, this); 133 } 134 135 /** 136 * @see NativeCrypto.SSLHandshakeCallbacks#clientPSKKeyRequested(String, byte[], byte[]) 137 */ 138 @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package 139 int clientPSKKeyRequested(String identityHint, byte[] identityBytesOut, byte[] key) { 140 PSKKeyManager pskKeyManager = parameters.getPSKKeyManager(); 141 if (pskKeyManager == null) { 142 return 0; 143 } 144 145 String identity = pskCallbacks.chooseClientPSKIdentity(pskKeyManager, identityHint); 146 // Store identity in NULL-terminated modified UTF-8 representation into ientityBytesOut 147 byte[] identityBytes; 148 if (identity == null) { 149 identity = ""; 150 identityBytes = EmptyArray.BYTE; 151 } else if (identity.isEmpty()) { 152 identityBytes = EmptyArray.BYTE; 153 } else { 154 try { 155 identityBytes = identity.getBytes("UTF-8"); 156 } catch (UnsupportedEncodingException e) { 157 throw new RuntimeException("UTF-8 encoding not supported", e); 158 } 159 } 160 if (identityBytes.length + 1 > identityBytesOut.length) { 161 // Insufficient space in the output buffer 162 return 0; 163 } 164 if (identityBytes.length > 0) { 165 System.arraycopy(identityBytes, 0, identityBytesOut, 0, identityBytes.length); 166 } 167 identityBytesOut[identityBytes.length] = 0; 168 169 SecretKey secretKey = pskCallbacks.getPSKKey(pskKeyManager, identityHint, identity); 170 byte[] secretKeyBytes = secretKey.getEncoded(); 171 if (secretKeyBytes == null) { 172 return 0; 173 } else if (secretKeyBytes.length > key.length) { 174 // Insufficient space in the output buffer 175 return 0; 176 } 177 System.arraycopy(secretKeyBytes, 0, key, 0, secretKeyBytes.length); 178 return secretKeyBytes.length; 179 } 180 181 /** 182 * @see NativeCrypto.SSLHandshakeCallbacks#serverPSKKeyRequested(String, String, byte[]) 183 */ 184 @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package 185 int serverPSKKeyRequested(String identityHint, String identity, byte[] key) { 186 PSKKeyManager pskKeyManager = parameters.getPSKKeyManager(); 187 if (pskKeyManager == null) { 188 return 0; 189 } 190 SecretKey secretKey = pskCallbacks.getPSKKey(pskKeyManager, identityHint, identity); 191 byte[] secretKeyBytes = secretKey.getEncoded(); 192 if (secretKeyBytes == null) { 193 return 0; 194 } else if (secretKeyBytes.length > key.length) { 195 return 0; 196 } 197 System.arraycopy(secretKeyBytes, 0, key, 0, secretKeyBytes.length); 198 return secretKeyBytes.length; 199 } 200 201 void chooseClientCertificate(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) 202 throws SSLException, CertificateEncodingException { 203 Set<String> keyTypesSet = SSLUtils.getSupportedClientKeyTypes(keyTypeBytes); 204 String[] keyTypes = keyTypesSet.toArray(new String[keyTypesSet.size()]); 205 206 X500Principal[] issuers; 207 if (asn1DerEncodedPrincipals == null) { 208 issuers = null; 209 } else { 210 issuers = new X500Principal[asn1DerEncodedPrincipals.length]; 211 for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) { 212 issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]); 213 } 214 } 215 X509KeyManager keyManager = parameters.getX509KeyManager(); 216 String alias = (keyManager != null) 217 ? aliasChooser.chooseClientAlias(keyManager, issuers, keyTypes) 218 : null; 219 setCertificate(alias); 220 } 221 222 void setCertificate(String alias) throws CertificateEncodingException, SSLException { 223 if (alias == null) { 224 return; 225 } 226 X509KeyManager keyManager = parameters.getX509KeyManager(); 227 if (keyManager == null) { 228 return; 229 } 230 PrivateKey privateKey = keyManager.getPrivateKey(alias); 231 if (privateKey == null) { 232 return; 233 } 234 localCertificates = keyManager.getCertificateChain(alias); 235 if (localCertificates == null) { 236 return; 237 } 238 int numLocalCerts = localCertificates.length; 239 PublicKey publicKey = (numLocalCerts > 0) ? localCertificates[0].getPublicKey() : null; 240 241 // Encode the local certificates. 242 byte[][] encodedLocalCerts = new byte[numLocalCerts][]; 243 for (int i = 0; i < numLocalCerts; ++i) { 244 encodedLocalCerts[i] = localCertificates[i].getEncoded(); 245 } 246 247 // Convert the key so we can access a native reference. 248 final OpenSSLKey key; 249 try { 250 key = OpenSSLKey.fromPrivateKeyForTLSStackOnly(privateKey, publicKey); 251 } catch (InvalidKeyException e) { 252 throw new SSLException(e); 253 } 254 255 // Set the local certs and private key. 256 NativeCrypto.setLocalCertsAndPrivateKey(ssl, this, encodedLocalCerts, key.getNativeRef()); 257 } 258 259 String getVersion() { 260 return NativeCrypto.SSL_get_version(ssl, this); 261 } 262 263 String getRequestedServerName() { 264 return NativeCrypto.SSL_get_servername(ssl, this); 265 } 266 267 byte[] getTlsChannelId() throws SSLException { 268 return NativeCrypto.SSL_get_tls_channel_id(ssl, this); 269 } 270 271 void initialize(String hostname, OpenSSLKey channelIdPrivateKey) throws IOException { 272 boolean enableSessionCreation = parameters.getEnableSessionCreation(); 273 if (!enableSessionCreation) { 274 NativeCrypto.SSL_set_session_creation_enabled(ssl, this, false); 275 } 276 277 // Allow servers to trigger renegotiation. Some inadvisable server 278 // configurations cause them to attempt to renegotiate during 279 // certain protocols. 280 NativeCrypto.SSL_accept_renegotiations(ssl, this); 281 282 if (isClient()) { 283 NativeCrypto.SSL_set_connect_state(ssl, this); 284 285 // Configure OCSP and CT extensions for client 286 NativeCrypto.SSL_enable_ocsp_stapling(ssl, this); 287 if (parameters.isCTVerificationEnabled(hostname)) { 288 NativeCrypto.SSL_enable_signed_cert_timestamps(ssl, this); 289 } 290 } else { 291 NativeCrypto.SSL_set_accept_state(ssl, this); 292 293 // Configure OCSP for server 294 if (parameters.getOCSPResponse() != null) { 295 NativeCrypto.SSL_enable_ocsp_stapling(ssl, this); 296 } 297 } 298 299 if (parameters.getEnabledProtocols().length == 0 && parameters.isEnabledProtocolsFiltered) { 300 throw new SSLHandshakeException("No enabled protocols; " 301 + NativeCrypto.OBSOLETE_PROTOCOL_SSLV3 302 + " is no longer supported and was filtered from the list"); 303 } 304 NativeCrypto.setEnabledProtocols(ssl, this, parameters.enabledProtocols); 305 NativeCrypto.setEnabledCipherSuites(ssl, this, parameters.enabledCipherSuites); 306 307 if (parameters.applicationProtocols.length > 0) { 308 NativeCrypto.setApplicationProtocols(ssl, this, isClient(), parameters.applicationProtocols); 309 } 310 if (!isClient() && parameters.applicationProtocolSelector != null) { 311 NativeCrypto.setApplicationProtocolSelector(ssl, this, parameters.applicationProtocolSelector); 312 } 313 314 // setup server certificates and private keys. 315 // clients will receive a call back to request certificates. 316 if (!isClient()) { 317 Set<String> keyTypes = new HashSet<String>(); 318 for (long sslCipherNativePointer : NativeCrypto.SSL_get_ciphers(ssl, this)) { 319 String keyType = SSLUtils.getServerX509KeyType(sslCipherNativePointer); 320 if (keyType != null) { 321 keyTypes.add(keyType); 322 } 323 } 324 X509KeyManager keyManager = parameters.getX509KeyManager(); 325 if (keyManager != null) { 326 for (String keyType : keyTypes) { 327 try { 328 setCertificate(aliasChooser.chooseServerAlias(keyManager, keyType)); 329 } catch (CertificateEncodingException e) { 330 throw new IOException(e); 331 } 332 } 333 } 334 335 NativeCrypto.SSL_set_options(ssl, this, SSL_OP_CIPHER_SERVER_PREFERENCE); 336 337 if (parameters.sctExtension != null) { 338 NativeCrypto.SSL_set_signed_cert_timestamp_list(ssl, this, parameters.sctExtension); 339 } 340 341 if (parameters.ocspResponse != null) { 342 NativeCrypto.SSL_set_ocsp_response(ssl, this, parameters.ocspResponse); 343 } 344 } 345 346 enablePSKKeyManagerIfRequested(); 347 348 if (parameters.useSessionTickets) { 349 NativeCrypto.SSL_clear_options(ssl, this, SSL_OP_NO_TICKET); 350 } else { 351 NativeCrypto.SSL_set_options( 352 ssl, this, NativeCrypto.SSL_get_options(ssl, this) | SSL_OP_NO_TICKET); 353 } 354 355 if (parameters.getUseSni() && AddressUtils.isValidSniHostname(hostname)) { 356 NativeCrypto.SSL_set_tlsext_host_name(ssl, this, hostname); 357 } 358 359 // BEAST attack mitigation (1/n-1 record splitting for CBC cipher suites 360 // with TLSv1 and SSLv3). 361 NativeCrypto.SSL_set_mode(ssl, this, SSL_MODE_CBC_RECORD_SPLITTING); 362 363 setCertificateValidation(); 364 setTlsChannelId(channelIdPrivateKey); 365 } 366 367 // TODO(nathanmittler): Remove once after we switch to the engine socket. 368 void doHandshake(FileDescriptor fd, int timeoutMillis) 369 throws CertificateException, IOException { 370 lock.readLock().lock(); 371 try { 372 if (isClosed() || fd == null || !fd.valid()) { 373 throw new SocketException("Socket is closed"); 374 } 375 NativeCrypto.SSL_do_handshake(ssl, this, fd, handshakeCallbacks, timeoutMillis); 376 } finally { 377 lock.readLock().unlock(); 378 } 379 } 380 381 int doHandshake() throws IOException { 382 lock.readLock().lock(); 383 try { 384 return NativeCrypto.ENGINE_SSL_do_handshake(ssl, this, handshakeCallbacks); 385 } finally { 386 lock.readLock().unlock(); 387 } 388 } 389 390 // TODO(nathanmittler): Remove once after we switch to the engine socket. 391 int read(FileDescriptor fd, byte[] buf, int offset, int len, int timeoutMillis) 392 throws IOException { 393 lock.readLock().lock(); 394 try { 395 if (isClosed() || fd == null || !fd.valid()) { 396 throw new SocketException("Socket is closed"); 397 } 398 return NativeCrypto 399 .SSL_read(ssl, this, fd, handshakeCallbacks, buf, offset, len, timeoutMillis); 400 } finally { 401 lock.readLock().unlock(); 402 } 403 } 404 405 // TODO(nathanmittler): Remove once after we switch to the engine socket. 406 void write(FileDescriptor fd, byte[] buf, int offset, int len, int timeoutMillis) 407 throws IOException { 408 lock.readLock().lock(); 409 try { 410 if (isClosed() || fd == null || !fd.valid()) { 411 throw new SocketException("Socket is closed"); 412 } 413 NativeCrypto 414 .SSL_write(ssl, this, fd, handshakeCallbacks, buf, offset, len, timeoutMillis); 415 } finally { 416 lock.readLock().unlock(); 417 } 418 } 419 420 @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package 421 private void enablePSKKeyManagerIfRequested() throws SSLException { 422 // Enable Pre-Shared Key (PSK) key exchange if requested 423 PSKKeyManager pskKeyManager = parameters.getPSKKeyManager(); 424 if (pskKeyManager != null) { 425 boolean pskEnabled = false; 426 for (String enabledCipherSuite : parameters.enabledCipherSuites) { 427 if ((enabledCipherSuite != null) && (enabledCipherSuite.contains("PSK"))) { 428 pskEnabled = true; 429 break; 430 } 431 } 432 if (pskEnabled) { 433 if (isClient()) { 434 NativeCrypto.set_SSL_psk_client_callback_enabled(ssl, this, true); 435 } else { 436 NativeCrypto.set_SSL_psk_server_callback_enabled(ssl, this, true); 437 String identityHint = pskCallbacks.chooseServerPSKIdentityHint(pskKeyManager); 438 NativeCrypto.SSL_use_psk_identity_hint(ssl, this, identityHint); 439 } 440 } 441 } 442 } 443 444 private void setTlsChannelId(OpenSSLKey channelIdPrivateKey) throws SSLException { 445 if (!parameters.channelIdEnabled) { 446 return; 447 } 448 449 if (parameters.getUseClientMode()) { 450 // Client-side TLS Channel ID 451 if (channelIdPrivateKey == null) { 452 throw new SSLHandshakeException("Invalid TLS channel ID key specified"); 453 } 454 NativeCrypto.SSL_set1_tls_channel_id(ssl, this, channelIdPrivateKey.getNativeRef()); 455 } else { 456 // Server-side TLS Channel ID 457 NativeCrypto.SSL_enable_tls_channel_id(ssl, this); 458 } 459 } 460 461 private void setCertificateValidation() throws SSLException { 462 // setup peer certificate verification 463 if (!isClient()) { 464 // needing client auth takes priority... 465 boolean certRequested; 466 if (parameters.getNeedClientAuth()) { 467 NativeCrypto.SSL_set_verify(ssl, this, SSL_VERIFY_PEER 468 | SSL_VERIFY_FAIL_IF_NO_PEER_CERT); 469 certRequested = true; 470 // ... over just wanting it... 471 } else if (parameters.getWantClientAuth()) { 472 NativeCrypto.SSL_set_verify(ssl, this, SSL_VERIFY_PEER); 473 certRequested = true; 474 // ... and we must disable verification if we don't want client auth. 475 } else { 476 NativeCrypto.SSL_set_verify(ssl, this, SSL_VERIFY_NONE); 477 certRequested = false; 478 } 479 480 if (certRequested) { 481 X509TrustManager trustManager = parameters.getX509TrustManager(); 482 X509Certificate[] issuers = trustManager.getAcceptedIssuers(); 483 if (issuers != null && issuers.length != 0) { 484 byte[][] issuersBytes; 485 try { 486 issuersBytes = SSLUtils.encodeIssuerX509Principals(issuers); 487 } catch (CertificateEncodingException e) { 488 throw new SSLException("Problem encoding principals", e); 489 } 490 NativeCrypto.SSL_set_client_CA_list(ssl, this, issuersBytes); 491 } 492 } 493 } 494 } 495 496 void interrupt() { 497 NativeCrypto.SSL_interrupt(ssl, this); 498 } 499 500 // TODO(nathanmittler): Remove once after we switch to the engine socket. 501 void shutdown(FileDescriptor fd) throws IOException { 502 NativeCrypto.SSL_shutdown(ssl, this, fd, handshakeCallbacks); 503 } 504 505 void shutdown() throws IOException { 506 NativeCrypto.ENGINE_SSL_shutdown(ssl, this, handshakeCallbacks); 507 } 508 509 boolean wasShutdownReceived() { 510 return (NativeCrypto.SSL_get_shutdown(ssl, this) & SSL_RECEIVED_SHUTDOWN) != 0; 511 } 512 513 boolean wasShutdownSent() { 514 return (NativeCrypto.SSL_get_shutdown(ssl, this) & SSL_SENT_SHUTDOWN) != 0; 515 } 516 517 int readDirectByteBuffer(long destAddress, int destLength) 518 throws IOException, CertificateException { 519 lock.readLock().lock(); 520 try { 521 return NativeCrypto.ENGINE_SSL_read_direct( 522 ssl, this, destAddress, destLength, handshakeCallbacks); 523 } finally { 524 lock.readLock().unlock(); 525 } 526 } 527 528 int writeDirectByteBuffer(long sourceAddress, int sourceLength) throws IOException { 529 lock.readLock().lock(); 530 try { 531 return NativeCrypto.ENGINE_SSL_write_direct( 532 ssl, this, sourceAddress, sourceLength, handshakeCallbacks); 533 } finally { 534 lock.readLock().unlock(); 535 } 536 } 537 538 int getPendingReadableBytes() { 539 return NativeCrypto.SSL_pending_readable_bytes(ssl, this); 540 } 541 542 int getMaxSealOverhead() { 543 return NativeCrypto.SSL_max_seal_overhead(ssl, this); 544 } 545 546 void close() { 547 lock.writeLock().lock(); 548 try { 549 if (!isClosed()) { 550 long toFree = ssl; 551 ssl = 0L; 552 NativeCrypto.SSL_free(toFree, this); 553 } 554 } finally { 555 lock.writeLock().unlock(); 556 } 557 } 558 559 boolean isClosed() { 560 return ssl == 0L; 561 } 562 563 int getError(int result) { 564 return NativeCrypto.SSL_get_error(ssl, this, result); 565 } 566 567 byte[] getApplicationProtocol() { 568 return NativeCrypto.getApplicationProtocol(ssl, this); 569 } 570 571 private boolean isClient() { 572 return parameters.getUseClientMode(); 573 } 574 575 @Override 576 protected final void finalize() throws Throwable { 577 try { 578 close(); 579 } finally { 580 super.finalize(); 581 } 582 } 583 584 /** 585 * A utility wrapper that abstracts operations on the underlying native BIO instance. 586 */ 587 final class BioWrapper { 588 private long bio; 589 590 private BioWrapper() throws SSLException { 591 this.bio = NativeCrypto.SSL_BIO_new(ssl, NativeSsl.this); 592 } 593 594 int getPendingWrittenBytes() { 595 return NativeCrypto.SSL_pending_written_bytes_in_BIO(bio); 596 } 597 598 int writeDirectByteBuffer(long address, int length) throws IOException { 599 return NativeCrypto.ENGINE_SSL_write_BIO_direct( 600 ssl, NativeSsl.this, bio, address, length, handshakeCallbacks); 601 } 602 603 int readDirectByteBuffer(long destAddress, int destLength) throws IOException { 604 return NativeCrypto.ENGINE_SSL_read_BIO_direct( 605 ssl, NativeSsl.this, bio, destAddress, destLength, handshakeCallbacks); 606 } 607 608 void close() { 609 NativeCrypto.BIO_free_all(bio); 610 bio = 0L; 611 } 612 } 613 } 614