1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package org.conscrypt;
19
20import java.security.KeyManagementException;
21import java.security.KeyStore;
22import java.security.KeyStoreException;
23import java.security.NoSuchAlgorithmException;
24import java.security.SecureRandom;
25import java.security.UnrecoverableKeyException;
26import java.util.Arrays;
27import javax.net.ssl.KeyManager;
28import javax.net.ssl.KeyManagerFactory;
29import javax.net.ssl.TrustManager;
30import javax.net.ssl.TrustManagerFactory;
31import javax.net.ssl.X509KeyManager;
32import javax.net.ssl.X509TrustManager;
33
34/**
35 * The instances of this class encapsulate all the info
36 * about enabled cipher suites and protocols,
37 * as well as the information about client/server mode of
38 * ssl socket, whether it require/want client authentication or not,
39 * and controls whether new SSL sessions may be established by this
40 * socket or not.
41 */
42public class SSLParametersImpl implements Cloneable {
43
44    // default source of authentication keys
45    private static volatile X509KeyManager defaultKeyManager;
46    // default source of authentication trust decisions
47    private static volatile X509TrustManager defaultTrustManager;
48    // default source of random numbers
49    private static volatile SecureRandom defaultSecureRandom;
50    // default SSL parameters
51    private static volatile SSLParametersImpl defaultParameters;
52
53    // client session context contains the set of reusable
54    // client-side SSL sessions
55    private final ClientSessionContext clientSessionContext;
56    // server session context contains the set of reusable
57    // server-side SSL sessions
58    private final ServerSessionContext serverSessionContext;
59    // source of authentication keys
60    private X509KeyManager keyManager;
61    // source of authentication trust decisions
62    private X509TrustManager trustManager;
63    // source of random numbers
64    private SecureRandom secureRandom;
65
66    // cipher suites available for SSL connection
67    private CipherSuite[] enabledCipherSuites;
68    // string representations of available cipher suites
69    private String[] enabledCipherSuiteNames = null;
70
71    // protocols available for SSL connection
72    private String[] enabledProtocols = ProtocolVersion.supportedProtocols;
73
74    // if the peer with this parameters tuned to work in client mode
75    private boolean client_mode = true;
76    // if the peer with this parameters tuned to require client authentication
77    private boolean need_client_auth = false;
78    // if the peer with this parameters tuned to request client authentication
79    private boolean want_client_auth = false;
80    // if the peer with this parameters allowed to cteate new SSL session
81    private boolean enable_session_creation = true;
82
83    protected CipherSuite[] getEnabledCipherSuitesMember() {
84        if (enabledCipherSuites == null) {
85            this.enabledCipherSuites = CipherSuite.DEFAULT_CIPHER_SUITES;
86        }
87        return enabledCipherSuites;
88    }
89
90    /**
91     * Initializes the parameters. Naturally this constructor is used
92     * in SSLContextImpl.engineInit method which directly passes its
93     * parameters. In other words this constructor holds all
94     * the functionality provided by SSLContext.init method.
95     * See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[],
96     * SecureRandom)} for more information
97     */
98    protected SSLParametersImpl(KeyManager[] kms, TrustManager[] tms,
99            SecureRandom sr, ClientSessionContext clientSessionContext,
100            ServerSessionContext serverSessionContext)
101            throws KeyManagementException {
102        this.serverSessionContext = serverSessionContext;
103        this.clientSessionContext = clientSessionContext;
104
105        // It's not described by the spec of SSLContext what should happen
106        // if the arrays of length 0 are specified. This implementation
107        // behave as for null arrays (i.e. use installed security providers)
108
109        // initialize keyManager
110        if ((kms == null) || (kms.length == 0)) {
111            keyManager = getDefaultKeyManager();
112        } else {
113            keyManager = findX509KeyManager(kms);
114        }
115
116        // initialize trustManager
117        if ((tms == null) || (tms.length == 0)) {
118            trustManager = getDefaultTrustManager();
119        } else {
120            trustManager = findX509TrustManager(tms);
121        }
122        // initialize secure random
123        // BEGIN android-removed
124        // if (sr == null) {
125        //     if (defaultSecureRandom == null) {
126        //         defaultSecureRandom = new SecureRandom();
127        //     }
128        //     secureRandom = defaultSecureRandom;
129        // } else {
130        //     secureRandom = sr;
131        // }
132        // END android-removed
133        // BEGIN android-added
134        // We simply use the SecureRandom passed in by the caller. If it's
135        // null, we don't replace it by a new instance. The native code below
136        // then directly accesses /dev/urandom. Not the most elegant solution,
137        // but faster than going through the SecureRandom object.
138        secureRandom = sr;
139        // END android-added
140    }
141
142    protected static SSLParametersImpl getDefault() throws KeyManagementException {
143        SSLParametersImpl result = defaultParameters;
144        if (result == null) {
145            // single-check idiom
146            defaultParameters = result = new SSLParametersImpl(null,
147                                                               null,
148                                                               null,
149                                                               new ClientSessionContext(),
150                                                               new ServerSessionContext());
151        }
152        return (SSLParametersImpl) result.clone();
153    }
154
155    /**
156     * @return server session context
157     */
158    protected ServerSessionContext getServerSessionContext() {
159        return serverSessionContext;
160    }
161
162    /**
163     * @return client session context
164     */
165    protected ClientSessionContext getClientSessionContext() {
166        return clientSessionContext;
167    }
168
169    /**
170     * @return key manager
171     */
172    protected X509KeyManager getKeyManager() {
173        return keyManager;
174    }
175
176    /**
177     * @return trust manager
178     */
179    protected X509TrustManager getTrustManager() {
180        return trustManager;
181    }
182
183    /**
184     * @return secure random
185     */
186    protected SecureRandom getSecureRandom() {
187        if (secureRandom != null) {
188            return secureRandom;
189        }
190        SecureRandom result = defaultSecureRandom;
191        if (result == null) {
192            // single-check idiom
193            defaultSecureRandom = result = new SecureRandom();
194        }
195        secureRandom = result;
196        return secureRandom;
197    }
198
199    /**
200     * @return the secure random member reference, even it is null
201     */
202    protected SecureRandom getSecureRandomMember() {
203        return secureRandom;
204    }
205
206    /**
207     * @return the names of enabled cipher suites
208     */
209    protected String[] getEnabledCipherSuites() {
210        if (enabledCipherSuiteNames == null) {
211            CipherSuite[] enabledCipherSuites = getEnabledCipherSuitesMember();
212            enabledCipherSuiteNames = new String[enabledCipherSuites.length];
213            for (int i = 0; i< enabledCipherSuites.length; i++) {
214                enabledCipherSuiteNames[i] = enabledCipherSuites[i].getName();
215            }
216        }
217        return enabledCipherSuiteNames.clone();
218    }
219
220    /**
221     * Sets the set of available cipher suites for use in SSL connection.
222     * @param   suites: String[]
223     * @return
224     */
225    protected void setEnabledCipherSuites(String[] suites) {
226        if (suites == null) {
227            throw new IllegalArgumentException("suites == null");
228        }
229        CipherSuite[] cipherSuites = new CipherSuite[suites.length];
230        for (int i=0; i<suites.length; i++) {
231            String suite = suites[i];
232            if (suite == null) {
233                throw new IllegalArgumentException("suites[" + i + "] == null");
234            }
235            cipherSuites[i] = CipherSuite.getByName(suite);
236            if (cipherSuites[i] == null || !cipherSuites[i].supported) {
237                throw new IllegalArgumentException(suite + " is not supported.");
238            }
239        }
240        enabledCipherSuites = cipherSuites;
241        enabledCipherSuiteNames = suites;
242    }
243
244    /**
245     * @return the set of enabled protocols
246     */
247    protected String[] getEnabledProtocols() {
248        return enabledProtocols.clone();
249    }
250
251    /**
252     * Sets the set of available protocols for use in SSL connection.
253     * @param protocols String[]
254     */
255    protected void setEnabledProtocols(String[] protocols) {
256        if (protocols == null) {
257            throw new IllegalArgumentException("protocols == null");
258        }
259        for (int i=0; i<protocols.length; i++) {
260            String protocol = protocols[i];
261            if (protocol == null) {
262                throw new IllegalArgumentException("protocols[" + i + "] == null");
263            }
264            if (!ProtocolVersion.isSupported(protocol)) {
265                throw new IllegalArgumentException("Protocol " + protocol + " is not supported.");
266            }
267        }
268        enabledProtocols = protocols;
269    }
270
271    /**
272     * Tunes the peer holding this parameters to work in client mode.
273     * @param   mode if the peer is configured to work in client mode
274     */
275    protected void setUseClientMode(boolean mode) {
276        client_mode = mode;
277    }
278
279    /**
280     * Returns the value indicating if the parameters configured to work
281     * in client mode.
282     */
283    protected boolean getUseClientMode() {
284        return client_mode;
285    }
286
287    /**
288     * Tunes the peer holding this parameters to require client authentication
289     */
290    protected void setNeedClientAuth(boolean need) {
291        need_client_auth = need;
292        // reset the want_client_auth setting
293        want_client_auth = false;
294    }
295
296    /**
297     * Returns the value indicating if the peer with this parameters tuned
298     * to require client authentication
299     */
300    protected boolean getNeedClientAuth() {
301        return need_client_auth;
302    }
303
304    /**
305     * Tunes the peer holding this parameters to request client authentication
306     */
307    protected void setWantClientAuth(boolean want) {
308        want_client_auth = want;
309        // reset the need_client_auth setting
310        need_client_auth = false;
311    }
312
313    /**
314     * Returns the value indicating if the peer with this parameters
315     * tuned to request client authentication
316     * @return
317     */
318    protected boolean getWantClientAuth() {
319        return want_client_auth;
320    }
321
322    /**
323     * Allows/disallows the peer holding this parameters to
324     * create new SSL session
325     */
326    protected void setEnableSessionCreation(boolean flag) {
327        enable_session_creation = flag;
328    }
329
330    /**
331     * Returns the value indicating if the peer with this parameters
332     * allowed to cteate new SSL session
333     */
334    protected boolean getEnableSessionCreation() {
335        return enable_session_creation;
336    }
337
338    /**
339     * Returns the clone of this object.
340     * @return the clone.
341     */
342    @Override
343    protected Object clone() {
344        try {
345            return super.clone();
346        } catch (CloneNotSupportedException e) {
347            throw new AssertionError(e);
348        }
349    }
350
351    private static X509KeyManager getDefaultKeyManager() throws KeyManagementException {
352        X509KeyManager result = defaultKeyManager;
353        if (result == null) {
354            // single-check idiom
355            defaultKeyManager = result = createDefaultKeyManager();
356        }
357        return result;
358    }
359    private static X509KeyManager createDefaultKeyManager() throws KeyManagementException {
360        try {
361            String algorithm = KeyManagerFactory.getDefaultAlgorithm();
362            KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
363            kmf.init(null, null);
364            KeyManager[] kms = kmf.getKeyManagers();
365            return findX509KeyManager(kms);
366        } catch (NoSuchAlgorithmException e) {
367            throw new KeyManagementException(e);
368        } catch (KeyStoreException e) {
369            throw new KeyManagementException(e);
370        } catch (UnrecoverableKeyException e) {
371            throw new KeyManagementException(e);
372        }
373    }
374    private static X509KeyManager findX509KeyManager(KeyManager[] kms) throws KeyManagementException {
375        for (KeyManager km : kms) {
376            if (km instanceof X509KeyManager) {
377                return (X509KeyManager)km;
378            }
379        }
380        throw new KeyManagementException("Failed to find an X509KeyManager in " + Arrays.toString(kms));
381    }
382
383    /**
384     * Gets the default trust manager.
385     *
386     * TODO: Move this to a published API under dalvik.system.
387     */
388    public static X509TrustManager getDefaultTrustManager() throws KeyManagementException {
389        X509TrustManager result = defaultTrustManager;
390        if (result == null) {
391            // single-check idiom
392            defaultTrustManager = result = createDefaultTrustManager();
393        }
394        return result;
395    }
396    private static X509TrustManager createDefaultTrustManager() throws KeyManagementException {
397        try {
398            String algorithm = TrustManagerFactory.getDefaultAlgorithm();
399            TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
400            tmf.init((KeyStore) null);
401            TrustManager[] tms = tmf.getTrustManagers();
402            X509TrustManager trustManager = findX509TrustManager(tms);
403            return trustManager;
404        } catch (NoSuchAlgorithmException e) {
405            throw new KeyManagementException(e);
406        } catch (KeyStoreException e) {
407            throw new KeyManagementException(e);
408        }
409    }
410    private static X509TrustManager findX509TrustManager(TrustManager[] tms) throws KeyManagementException {
411        for (TrustManager tm : tms) {
412            if (tm instanceof X509TrustManager) {
413                return (X509TrustManager)tm;
414            }
415        }
416        throw new KeyManagementException("Failed to find an X509TrustManager in " +  Arrays.toString(tms));
417    }
418}
419