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
17package org.conscrypt;
18
19import java.security.Principal;
20import java.security.cert.Certificate;
21import java.util.HashMap;
22import java.util.List;
23import javax.net.ssl.SSLPeerUnverifiedException;
24import javax.net.ssl.SSLSession;
25import javax.net.ssl.SSLSessionBindingEvent;
26import javax.net.ssl.SSLSessionBindingListener;
27import javax.net.ssl.SSLSessionContext;
28import javax.security.cert.X509Certificate;
29
30/**
31 * An externalized view of the underlying {@link SSLSession} used within a
32 * socket/engine. This class provides the caller with a consistent session
33 * handle which will continue to be usable regardless of internal changes
34 * to the connection.  It does this by delegating calls to the <b>current</b>
35 * internal session, which is provided by the session {@code Provider}
36 * (i.e. the socket or engine that owns the session).  This allows the provider
37 * to switch implementations (for instance, using a JNI implementation to
38 * access live values while the connection is open and a set of final values
39 * when the connection is closed), even if the caller stores a reference to
40 * the session object.
41 *
42 * <p>This class implements the {@link SSLSession} value API itself, rather
43 * than delegating to the provided session, to ensure the caller has a consistent
44 * value map, regardless of which internal session is currently being used by the
45 * socket/engine.  This class will never call the value API methods on the
46 * underlying sessions, so they need not be implemented.
47 */
48final class ExternalSession implements SessionDecorator {
49
50  // Use an initialcapacity of 2 to keep it small in the average case.
51  private final HashMap<String, Object> values = new HashMap<String, Object>(2);
52  private final Provider provider;
53
54  public ExternalSession(Provider provider) {
55    this.provider = provider;
56  }
57
58  @Override
59  public ConscryptSession getDelegate() {
60    return provider.provideSession();
61  }
62
63  @Override
64  public String getRequestedServerName() {
65    return getDelegate().getRequestedServerName();
66  }
67
68  @Override
69  public List<byte[]> getStatusResponses() {
70    return getDelegate().getStatusResponses();
71  }
72
73  @Override
74  public byte[] getPeerSignedCertificateTimestamp() {
75    return getDelegate().getPeerSignedCertificateTimestamp();
76  }
77
78  @Override
79  public byte[] getId() {
80    return getDelegate().getId();
81  }
82
83  @Override
84  public SSLSessionContext getSessionContext() {
85    return getDelegate().getSessionContext();
86  }
87
88  @Override
89  public long getCreationTime() {
90    return getDelegate().getCreationTime();
91  }
92
93  @Override
94  public long getLastAccessedTime() {
95    return getDelegate().getLastAccessedTime();
96  }
97
98  @Override
99  public void invalidate() {
100    getDelegate().invalidate();
101  }
102
103  @Override
104  public boolean isValid() {
105    return getDelegate().isValid();
106  }
107
108  @Override
109  public java.security.cert.X509Certificate[] getPeerCertificates()
110      throws SSLPeerUnverifiedException {
111    return getDelegate().getPeerCertificates();
112  }
113
114  @Override
115  public Certificate[] getLocalCertificates() {
116    return getDelegate().getLocalCertificates();
117  }
118
119  @Override
120  public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
121    return getDelegate().getPeerCertificateChain();
122  }
123
124  @Override
125  public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
126    return getDelegate().getPeerPrincipal();
127  }
128
129  @Override
130  public Principal getLocalPrincipal() {
131    return getDelegate().getLocalPrincipal();
132  }
133
134  @Override
135  public String getCipherSuite() {
136    return getDelegate().getCipherSuite();
137  }
138
139  @Override
140  public String getProtocol() {
141    return getDelegate().getProtocol();
142  }
143
144  @Override
145  public String getPeerHost() {
146    return getDelegate().getPeerHost();
147  }
148
149  @Override
150  public int getPeerPort() {
151    return getDelegate().getPeerPort();
152  }
153
154  @Override
155  public int getPacketBufferSize() {
156    return getDelegate().getPacketBufferSize();
157  }
158
159  @Override
160  public int getApplicationBufferSize() {
161    return getDelegate().getApplicationBufferSize();
162  }
163
164  @Override
165  public Object getValue(String name) {
166    if (name == null) {
167      throw new IllegalArgumentException("name == null");
168    }
169    return values.get(name);
170  }
171
172  @Override
173  public String[] getValueNames() {
174    return values.keySet().toArray(new String[values.size()]);
175  }
176
177  @Override
178  public void putValue(String name, Object value) {
179    if (name == null || value == null) {
180      throw new IllegalArgumentException("name == null || value == null");
181    }
182    Object old = values.put(name, value);
183    if (value instanceof SSLSessionBindingListener) {
184      ((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name));
185    }
186    if (old instanceof SSLSessionBindingListener) {
187      ((SSLSessionBindingListener) old).valueUnbound(new SSLSessionBindingEvent(this, name));
188    }
189
190  }
191
192  @Override
193  public void removeValue(String name) {
194    if (name == null) {
195      throw new IllegalArgumentException("name == null");
196    }
197    Object old = values.remove(name);
198    if (old instanceof SSLSessionBindingListener) {
199      SSLSessionBindingListener listener = (SSLSessionBindingListener) old;
200      listener.valueUnbound(new SSLSessionBindingEvent(this, name));
201    }
202  }
203
204  /**
205   * The provider of the current delegate session.
206   */
207  interface Provider {
208    ConscryptSession provideSession();
209  }
210}
211