Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2014 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 android.net; |
| 18 | |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 19 | import com.android.org.conscrypt.PSKKeyManager; |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 20 | import java.net.Socket; |
| 21 | import javax.crypto.SecretKey; |
| 22 | import javax.net.ssl.SSLEngine; |
| 23 | |
| 24 | /** |
| 25 | * Provider of key material for pre-shared key (PSK) key exchange used in TLS-PSK cipher suites. |
| 26 | * |
| 27 | * <h3>Overview of TLS-PSK</h3> |
| 28 | * |
| 29 | * <p>TLS-PSK is a set of TLS/SSL cipher suites which rely on a symmetric pre-shared key (PSK) to |
| 30 | * secure the TLS/SSL connection and mutually authenticate its peers. These cipher suites may be |
| 31 | * a more natural fit compared to conventional public key based cipher suites in some scenarios |
| 32 | * where communication between peers is bootstrapped via a separate step (for example, a pairing |
| 33 | * step) and requires both peers to authenticate each other. In such scenarios a symmetric key (PSK) |
| 34 | * can be exchanged during the bootstrapping step, removing the need to generate and exchange public |
| 35 | * key pairs and X.509 certificates.</p> |
| 36 | * |
| 37 | * <p>When a TLS-PSK cipher suite is used, both peers have to use the same key for the TLS/SSL |
| 38 | * handshake to succeed. Thus, both peers are implicitly authenticated by a successful handshake. |
| 39 | * This removes the need to use a {@code TrustManager} in conjunction with this {@code KeyManager}. |
| 40 | * </p> |
| 41 | * |
| 42 | * <h3>Supporting multiple keys</h3> |
| 43 | * |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 44 | * <p>A peer may have multiple keys to choose from. To help choose the right key, during the |
| 45 | * handshake the server can provide a <em>PSK identity hint</em> to the client, and the client can |
| 46 | * provide a <em>PSK identity</em> to the server. The contents of these two pieces of information |
| 47 | * are specific to application-level protocols.</p> |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 48 | * |
| 49 | * <p><em>NOTE: Both the PSK identity hint and the PSK identity are transmitted in cleartext. |
| 50 | * Moreover, these data are received and processed prior to peer having been authenticated. Thus, |
| 51 | * they must not contain or leak key material or other sensitive information, and should be |
| 52 | * treated (e.g., parsed) with caution, as untrusted data.</em></p> |
| 53 | * |
| 54 | * <p>The high-level flow leading to peers choosing a key during TLS/SSL handshake is as follows: |
| 55 | * <ol> |
| 56 | * <li>Server receives a handshake request from client. |
| 57 | * <li>Server replies, optionally providing a PSK identity hint to client.</li> |
| 58 | * <li>Client chooses the key.</li> |
| 59 | * <li>Client provides a PSK identity of the chosen key to server.</li> |
| 60 | * <li>Server chooses the key.</li> |
| 61 | * </ol></p> |
| 62 | * |
| 63 | * <p>In the flow above, either peer can signal that they do not have a suitable key, in which case |
| 64 | * the the handshake will be aborted immediately. This may enable a network attacker who does not |
| 65 | * know the key to learn which PSK identity hints or PSK identities are supported. If this is a |
| 66 | * concern then a randomly generated key should be used in the scenario where no key is available. |
| 67 | * This will lead to the handshake aborting later, due to key mismatch -- same as in the scenario |
| 68 | * where a key is available -- making it appear to the attacker that all PSK identity hints and PSK |
| 69 | * identities are supported.</p> |
| 70 | * |
| 71 | * <h3>Maximum sizes</h3> |
| 72 | * |
| 73 | * <p>The maximum supported sizes are as follows: |
| 74 | * <ul> |
| 75 | * <li>256 bytes for keys (see {@link #MAX_KEY_LENGTH_BYTES}),</li> |
| 76 | * <li>128 bytes for PSK identity and PSK identity hint (in modified UTF-8 representation) (see |
| 77 | * {@link #MAX_IDENTITY_LENGTH_BYTES} and {@link #MAX_IDENTITY_HINT_LENGTH_BYTES}).</li> |
| 78 | * </ul></p> |
| 79 | * |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 80 | * <h3>Subclassing</h3> |
| 81 | * Subclasses should normally provide their own implementation of {@code getKey} because the default |
| 82 | * implementation returns no key, which aborts the handshake. |
| 83 | * |
Alex Klyubin | 8e93f0c | 2014-10-30 13:33:58 -0700 | [diff] [blame] | 84 | * <h3>Known issues</h3> |
| 85 | * The implementation of {@code ECDHE_PSK} cipher suites in API Level 21 contains a bug which breaks |
| 86 | * compatibility with other implementations. {@code ECDHE_PSK} cipher suites are enabled by default |
| 87 | * on platforms with API Level 21 when an {@code SSLContext} is initialized with a |
| 88 | * {@code PskKeyManager}. A workaround is to disable {@code ECDHE_PSK} cipher suites on platforms |
| 89 | * with API Level 21. |
| 90 | * |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 91 | * <h3>Example</h3> |
| 92 | * The following example illustrates how to create an {@code SSLContext} which enables the use of |
| 93 | * TLS-PSK in {@code SSLSocket}, {@code SSLServerSocket} and {@code SSLEngine} instances obtained |
| 94 | * from it. |
| 95 | * <pre> {@code |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 96 | * PskKeyManager pskKeyManager = ...; |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 97 | * |
| 98 | * SSLContext sslContext = SSLContext.getInstance("TLS"); |
| 99 | * sslContext.init( |
Neil Fuller | 71fbb81 | 2015-11-30 09:51:33 +0000 | [diff] [blame] | 100 | * new KeyManager[] { pskKeyManager }, |
Alex Klyubin | b56f212 | 2014-07-15 10:38:21 -0700 | [diff] [blame] | 101 | * new TrustManager[0], // No TrustManagers needed for TLS-PSK |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 102 | * null // Use the default source of entropy |
| 103 | * ); |
| 104 | * |
| 105 | * SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(...); |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 106 | * }</pre> |
| 107 | */ |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 108 | public abstract class PskKeyManager implements PSKKeyManager { |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 109 | // IMPLEMENTATION DETAILS: This class exists only because the default implemenetation of the |
| 110 | // TLS/SSL JSSE provider (currently Conscrypt) cannot depend on Android framework classes. |
| 111 | // As a result, this framework class simply extends the PSKKeyManager interface from Conscrypt |
| 112 | // without adding any new methods or fields. Moreover, for technical reasons (Conscrypt classes |
| 113 | // are "hidden") this class replaces the Javadoc of Conscrypt's PSKKeyManager. |
| 114 | |
| 115 | /** |
| 116 | * Maximum supported length (in bytes) for PSK identity hint (in modified UTF-8 representation). |
| 117 | */ |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 118 | public static final int MAX_IDENTITY_HINT_LENGTH_BYTES = |
| 119 | PSKKeyManager.MAX_IDENTITY_HINT_LENGTH_BYTES; |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 120 | |
| 121 | /** Maximum supported length (in bytes) for PSK identity (in modified UTF-8 representation). */ |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 122 | public static final int MAX_IDENTITY_LENGTH_BYTES = PSKKeyManager.MAX_IDENTITY_LENGTH_BYTES; |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 123 | |
| 124 | /** Maximum supported length (in bytes) for PSK. */ |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 125 | public static final int MAX_KEY_LENGTH_BYTES = PSKKeyManager.MAX_KEY_LENGTH_BYTES; |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 126 | |
| 127 | /** |
| 128 | * Gets the PSK identity hint to report to the client to help agree on the PSK for the provided |
| 129 | * socket. |
| 130 | * |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 131 | * <p> |
| 132 | * The default implementation returns {@code null}. |
| 133 | * |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 134 | * @return PSK identity hint to be provided to the client or {@code null} to provide no hint. |
| 135 | */ |
| 136 | @Override |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 137 | public String chooseServerKeyIdentityHint(Socket socket) { |
| 138 | return null; |
| 139 | } |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 140 | |
| 141 | /** |
| 142 | * Gets the PSK identity hint to report to the client to help agree on the PSK for the provided |
| 143 | * engine. |
| 144 | * |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 145 | * <p> |
| 146 | * The default implementation returns {@code null}. |
| 147 | * |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 148 | * @return PSK identity hint to be provided to the client or {@code null} to provide no hint. |
| 149 | */ |
| 150 | @Override |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 151 | public String chooseServerKeyIdentityHint(SSLEngine engine) { |
| 152 | return null; |
| 153 | } |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 154 | |
| 155 | /** |
| 156 | * Gets the PSK identity to report to the server to help agree on the PSK for the provided |
| 157 | * socket. |
| 158 | * |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 159 | * <p> |
| 160 | * The default implementation returns an empty string. |
| 161 | * |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 162 | * @param identityHint identity hint provided by the server or {@code null} if none provided. |
| 163 | * |
| 164 | * @return PSK identity to provide to the server. {@code null} is permitted but will be |
| 165 | * converted into an empty string. |
| 166 | */ |
| 167 | @Override |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 168 | public String chooseClientKeyIdentity(String identityHint, Socket socket) { |
| 169 | return ""; |
| 170 | } |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 171 | |
| 172 | /** |
| 173 | * Gets the PSK identity to report to the server to help agree on the PSK for the provided |
| 174 | * engine. |
| 175 | * |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 176 | * <p> |
| 177 | * The default implementation returns an empty string. |
| 178 | * |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 179 | * @param identityHint identity hint provided by the server or {@code null} if none provided. |
| 180 | * |
| 181 | * @return PSK identity to provide to the server. {@code null} is permitted but will be |
| 182 | * converted into an empty string. |
| 183 | */ |
| 184 | @Override |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 185 | public String chooseClientKeyIdentity(String identityHint, SSLEngine engine) { |
| 186 | return ""; |
| 187 | } |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 188 | |
| 189 | /** |
| 190 | * Gets the PSK to use for the provided socket. |
| 191 | * |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 192 | * <p> |
| 193 | * The default implementation returns {@code null}. |
| 194 | * |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 195 | * @param identityHint identity hint provided by the server to help select the key or |
| 196 | * {@code null} if none provided. |
| 197 | * @param identity identity provided by the client to help select the key. |
| 198 | * |
| 199 | * @return key or {@code null} to signal to peer that no suitable key is available and to abort |
| 200 | * the handshake. |
| 201 | */ |
| 202 | @Override |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 203 | public SecretKey getKey(String identityHint, String identity, Socket socket) { |
| 204 | return null; |
| 205 | } |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 206 | |
| 207 | /** |
| 208 | * Gets the PSK to use for the provided engine. |
| 209 | * |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 210 | * <p> |
| 211 | * The default implementation returns {@code null}. |
| 212 | * |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 213 | * @param identityHint identity hint provided by the server to help select the key or |
| 214 | * {@code null} if none provided. |
| 215 | * @param identity identity provided by the client to help select the key. |
| 216 | * |
| 217 | * @return key or {@code null} to signal to peer that no suitable key is available and to abort |
| 218 | * the handshake. |
| 219 | */ |
| 220 | @Override |
Alex Klyubin | fcd8b20 | 2014-07-21 10:09:58 -0700 | [diff] [blame] | 221 | public SecretKey getKey(String identityHint, String identity, SSLEngine engine) { |
| 222 | return null; |
| 223 | } |
Alex Klyubin | b0d1d91 | 2014-06-10 10:28:08 -0700 | [diff] [blame] | 224 | } |