| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * Copyright 2016 The Netty Project |
| * |
| * The Netty Project licenses this file to you under the Apache License, |
| * version 2.0 (the "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at: |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.conscrypt; |
| |
| import static java.lang.Math.min; |
| import static org.conscrypt.NativeConstants.SSL3_RT_ALERT; |
| import static org.conscrypt.NativeConstants.SSL3_RT_APPLICATION_DATA; |
| import static org.conscrypt.NativeConstants.SSL3_RT_CHANGE_CIPHER_SPEC; |
| import static org.conscrypt.NativeConstants.SSL3_RT_HANDSHAKE; |
| import static org.conscrypt.NativeConstants.SSL3_RT_HEADER_LENGTH; |
| import static org.conscrypt.NativeConstants.SSL3_RT_MAX_PACKET_SIZE; |
| |
| import java.nio.ByteBuffer; |
| import javax.net.ssl.SSLException; |
| import javax.net.ssl.SSLHandshakeException; |
| |
| /** |
| * Utility methods for SSL packet processing. Copied from the Netty project. |
| * <p> |
| * This is a public class to allow testing to occur on Android via CTS. |
| */ |
| final class SSLUtils { |
| static final boolean USE_ENGINE_SOCKET_BY_DEFAULT = |
| Boolean.parseBoolean(System.getProperty("org.conscrypt.useEngineSocketByDefault")); |
| private static final int MAX_PROTOCOL_LENGTH = 255; |
| |
| /** |
| * States for SSL engines. |
| */ |
| static final class EngineStates { |
| private EngineStates() {} |
| |
| /** |
| * The engine is constructed, but the initial handshake hasn't been started |
| */ |
| static final int STATE_NEW = 0; |
| |
| /** |
| * The client/server mode of the engine has been set. |
| */ |
| static final int STATE_MODE_SET = 1; |
| |
| /** |
| * The handshake has been started |
| */ |
| static final int STATE_HANDSHAKE_STARTED = 2; |
| |
| /** |
| * Listeners of the handshake have been notified of completion but the handshake call |
| * hasn't returned. |
| */ |
| static final int STATE_HANDSHAKE_COMPLETED = 3; |
| |
| /** |
| * The handshake call returned but the listeners have not yet been notified. This is expected |
| * behaviour in cut-through mode, where SSL_do_handshake returns before the handshake is |
| * complete. We can now start writing data to the socket. |
| */ |
| static final int STATE_READY_HANDSHAKE_CUT_THROUGH = 4; |
| |
| /** |
| * The handshake call has returned and the listeners have been notified. Ready to begin |
| * writing data. |
| */ |
| static final int STATE_READY = 5; |
| |
| /** |
| * The inbound direction of the engine has been closed. |
| */ |
| static final int STATE_CLOSED_INBOUND = 6; |
| |
| /** |
| * The outbound direction of the engine has been closed. |
| */ |
| static final int STATE_CLOSED_OUTBOUND = 7; |
| |
| /** |
| * The engine has been closed. |
| */ |
| static final int STATE_CLOSED = 8; |
| } |
| |
| /** |
| * This is the maximum overhead when encrypting plaintext as defined by |
| * <a href="https://www.ietf.org/rfc/rfc5246.txt">rfc5264</a>, |
| * <a href="https://www.ietf.org/rfc/rfc5289.txt">rfc5289</a> and openssl implementation itself. |
| * |
| * Please note that we use a padding of 16 here as openssl uses PKC#5 which uses 16 bytes |
| * whilethe spec itself allow up to 255 bytes. 16 bytes is the max for PKC#5 (which handles it |
| * the same way as PKC#7) as we use a block size of 16. See <a |
| * href="https://tools.ietf.org/html/rfc5652#section-6.3">rfc5652#section-6.3</a>. |
| * |
| * 16 (IV) + 48 (MAC) + 1 (Padding_length field) + 15 (Padding) + 1 (ContentType) + 2 |
| * (ProtocolVersion) + 2 (Length) |
| * |
| * TODO: We may need to review this calculation once TLS 1.3 becomes available. |
| */ |
| private static final int MAX_ENCRYPTION_OVERHEAD_LENGTH = 15 + 48 + 1 + 16 + 1 + 2 + 2; |
| |
| private static final int MAX_ENCRYPTION_OVERHEAD_DIFF = |
| Integer.MAX_VALUE - MAX_ENCRYPTION_OVERHEAD_LENGTH; |
| |
| /** |
| * Calculates the minimum bytes required in the encrypted output buffer for the given number of |
| * plaintext source bytes. |
| */ |
| static int calculateOutNetBufSize(int pendingBytes) { |
| return min(SSL3_RT_MAX_PACKET_SIZE, |
| MAX_ENCRYPTION_OVERHEAD_LENGTH + min(MAX_ENCRYPTION_OVERHEAD_DIFF, pendingBytes)); |
| } |
| |
| /** |
| * Wraps the given exception if it's not already a {@link SSLHandshakeException}. |
| */ |
| static SSLHandshakeException toSSLHandshakeException(Throwable e) { |
| if (e instanceof SSLHandshakeException) { |
| return (SSLHandshakeException) e; |
| } |
| |
| return (SSLHandshakeException) new SSLHandshakeException(e.getMessage()).initCause(e); |
| } |
| |
| /** |
| * Wraps the given exception if it's not already a {@link SSLException}. |
| */ |
| static SSLException toSSLException(Throwable e) { |
| if (e instanceof SSLException) { |
| return (SSLException) e; |
| } |
| return new SSLException(e); |
| } |
| |
| /** |
| * Return how much bytes can be read out of the encrypted data. Be aware that this method will |
| * not |
| * increase the readerIndex of the given {@link ByteBuffer}. |
| * |
| * @param buffers The {@link ByteBuffer}s to read from. Be aware that they must have at least |
| * {@link org.conscrypt.NativeConstants#SSL3_RT_HEADER_LENGTH} bytes to read, otherwise it will |
| * throw an {@link IllegalArgumentException}. |
| * @return length The length of the encrypted packet that is included in the buffer. This will |
| * return {@code -1} if the given {@link ByteBuffer} is not encrypted at all. |
| * @throws IllegalArgumentException Is thrown if the given {@link ByteBuffer} has not at least |
| * {@link org.conscrypt.NativeConstants#SSL3_RT_HEADER_LENGTH} bytes to read. |
| */ |
| static int getEncryptedPacketLength(ByteBuffer[] buffers, int offset) { |
| ByteBuffer buffer = buffers[offset]; |
| |
| // Check if everything we need is in one ByteBuffer. If so we can make use of the fast-path. |
| if (buffer.remaining() >= SSL3_RT_HEADER_LENGTH) { |
| return getEncryptedPacketLength(buffer); |
| } |
| |
| // We need to copy 5 bytes into a temporary buffer so we can parse out the packet length |
| // easily. |
| ByteBuffer tmp = ByteBuffer.allocate(SSL3_RT_HEADER_LENGTH); |
| do { |
| buffer = buffers[offset++]; |
| int pos = buffer.position(); |
| int limit = buffer.limit(); |
| if (buffer.remaining() > tmp.remaining()) { |
| buffer.limit(pos + tmp.remaining()); |
| } |
| try { |
| tmp.put(buffer); |
| } finally { |
| // Restore the original indices. |
| buffer.limit(limit); |
| buffer.position(pos); |
| } |
| } while (tmp.hasRemaining()); |
| |
| // Done, flip the buffer so we can read from it. |
| tmp.flip(); |
| return getEncryptedPacketLength(tmp); |
| } |
| |
| /** |
| * Encodes a list of protocols into the wire-format (length-prefixed 8-bit strings). |
| * Requires that all strings be encoded with US-ASCII. |
| * |
| * @param protocols the list of protocols to be encoded |
| * @return the encoded form of the protocol list. |
| */ |
| static byte[] toLengthPrefixedList(String... protocols) { |
| // Calculate the encoded length. |
| int length = 0; |
| for (int i = 0; i < protocols.length; ++i) { |
| int protocolLength = protocols[i].length(); |
| |
| // Verify that the length is valid here, so that we don't attempt to allocate an array |
| // below if the threshold is violated. |
| if (protocolLength == 0 || protocolLength > MAX_PROTOCOL_LENGTH) { |
| throw new IllegalArgumentException("Protocol has invalid length (" |
| + protocolLength + "): " + protocols[i]); |
| } |
| |
| // Include a 1-byte prefix for each protocol. |
| length += 1 + protocolLength; |
| } |
| |
| byte[] data = new byte[length]; |
| for (int dataIndex = 0, i = 0; i < protocols.length; ++i) { |
| String protocol = protocols[i]; |
| int protocolLength = protocol.length(); |
| |
| // Add the length prefix. |
| data[dataIndex++] = (byte) protocolLength; |
| for (int ci = 0; ci < protocolLength; ++ci) { |
| char c = protocol.charAt(ci); |
| if (c > Byte.MAX_VALUE) { |
| // Enforce US-ASCII |
| throw new IllegalArgumentException("Protocol contains invalid character: " |
| + c + "(protocol=" + protocol + ")"); |
| } |
| data[dataIndex++] = (byte) c; |
| } |
| } |
| return data; |
| } |
| |
| private static int getEncryptedPacketLength(ByteBuffer buffer) { |
| int packetLength = 0; |
| int pos = buffer.position(); |
| // SSLv3 or TLS - Check ContentType |
| switch (unsignedByte(buffer.get(pos))) { |
| case SSL3_RT_CHANGE_CIPHER_SPEC: |
| case SSL3_RT_ALERT: |
| case SSL3_RT_HANDSHAKE: |
| case SSL3_RT_APPLICATION_DATA: |
| break; |
| default: |
| // SSLv2 or bad data |
| return -1; |
| } |
| |
| // SSLv3 or TLS - Check ProtocolVersion |
| int majorVersion = unsignedByte(buffer.get(pos + 1)); |
| if (majorVersion != 3) { |
| // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data) |
| return -1; |
| } |
| |
| // SSLv3 or TLS |
| packetLength = unsignedShort(buffer.getShort(pos + 3)) + SSL3_RT_HEADER_LENGTH; |
| if (packetLength <= SSL3_RT_HEADER_LENGTH) { |
| // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data) |
| return -1; |
| } |
| return packetLength; |
| } |
| |
| private static short unsignedByte(byte b) { |
| return (short) (b & 0xFF); |
| } |
| |
| private static int unsignedShort(short s) { |
| return s & 0xFFFF; |
| } |
| |
| private SSLUtils() {} |
| } |