1 /**
2 * TLS Session
3 * 
4 * Copyright:
5 * (C) 2011-2012 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.tls.session;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_TLS):
15 
16 import botan.cert.x509.x509cert;
17 import botan.tls.version_;
18 import botan.tls.ciphersuite;
19 import botan.tls.magic;
20 import botan.tls.server_info;
21 import memutils.vector;
22 import botan.algo_base.symkey;
23 import botan.asn1.der_enc;
24 import botan.asn1.ber_dec;
25 import botan.asn1.asn1_str;
26 import botan.codec.pem;
27 import botan.rng.rng;
28 import botan.constructs.cryptobox_psk;
29 import botan.utils.types;
30 import core.stdc.time : time_t;
31 import std.datetime;
32 
33 
34 /**
35 * Class representing a TLS session state
36 */
37 class TLSSession
38 {
39 public:
40     /**
41     * New session (sets session start time)
42     */
43     this(Vector!ubyte session_identifier,
44          SecureVector!ubyte master_secret,
45          SecureVector!ubyte orig_hs_hash,
46          TLSProtocolVersion _version,
47          ushort ciphersuite,
48          ubyte compression_method,
49          ConnectionSide side,
50          size_t fragment_size,
51          bool extended_master_secret,
52          Vector!X509Certificate certs,
53          Vector!ubyte ticket,
54          in TLSServerInformation server_info,
55          in string srp_identifier)
56     {
57         m_start_time = Clock.currTime(UTC());
58         m_identifier = session_identifier.move();
59         m_session_ticket = ticket.move();
60         m_master_secret = master_secret.move();
61         m_orig_hs_hash = orig_hs_hash.move();
62         m_version = _version;
63         m_ciphersuite = ciphersuite;
64         m_compression_method = compression_method;
65         m_connection_side = side;
66         m_fragment_size = fragment_size;
67         m_peer_certs = certs.move();
68         m_server_info = server_info;
69         m_srp_identifier = srp_identifier;
70 		m_extended_master_secret = extended_master_secret;
71     }
72 
73     /**
74     * Load a session from DER representation (created by DER_encode)
75     */
76     this(const(ubyte)* ber, size_t ber_len)
77     {
78         ubyte side_code = 0;
79         
80         ASN1String server_hostname;
81         ASN1String server_service;
82         size_t server_port;
83         
84         ASN1String srp_identifier_str;
85         
86         ubyte major_version = 0, minor_version = 0;
87         
88         Vector!ubyte peer_cert_bits;
89         
90         size_t start_time = 0;
91         
92         BERDecoder(ber, ber_len)
93                 .startCons(ASN1Tag.SEQUENCE)
94                 .decodeAndCheck(cast(size_t)(TLS_SESSION_PARAM_STRUCT_VERSION),
95                                   "Unknown version in session structure")
96                 .decodeIntegerType(start_time)
97                 .decodeIntegerType(major_version)
98                 .decodeIntegerType(minor_version)
99                 .decode(m_identifier, ASN1Tag.OCTET_STRING)
100                 .decode(m_session_ticket, ASN1Tag.OCTET_STRING)
101                 .decodeIntegerType(m_ciphersuite)
102                 .decodeIntegerType(m_compression_method)
103                 .decodeIntegerType(side_code)
104                 .decodeIntegerType(m_fragment_size)
105 				.decode(m_extended_master_secret)
106                 .decode(m_master_secret, ASN1Tag.OCTET_STRING)
107                 .decode(m_orig_hs_hash, ASN1Tag.OCTET_STRING)
108                 .decode(peer_cert_bits, ASN1Tag.OCTET_STRING)
109                 .decode(server_hostname)
110                 .decode(server_service)
111                 .decode(server_port)
112                 .decode(srp_identifier_str)
113                 .endCons()
114                 .verifyEnd();
115         
116         m_version = TLSProtocolVersion(major_version, minor_version);
117         m_start_time = SysTime(unixTimeToStdTime(cast(time_t)start_time));
118         m_connection_side = cast(ConnectionSide)(side_code);
119         
120         m_server_info = TLSServerInformation(server_hostname.value(),
121                                              server_service.value(),
122                                              cast(ushort) server_port);
123         
124         m_srp_identifier = srp_identifier_str.value();
125         
126         if (!peer_cert_bits.empty)
127         {
128             auto certs = DataSourceMemory(peer_cert_bits.ptr, peer_cert_bits.length);
129             while (!certs.endOfData())
130                 m_peer_certs.pushBack(X509Certificate(cast(DataSource)certs));
131         }
132     }
133 
134     /**
135     * Load a session from PEM representation (created by PEM_encode)
136     */
137     this(in string pem)
138     {
139         SecureVector!ubyte der = PEM.decodeCheckLabel(pem, "SSL SESSION");
140         
141         this(der.ptr, der.length);
142     }
143 
144     /**
145     * Encode this session data for storage
146     * Notes: if the master secret is compromised so is the session traffic
147     */
148     SecureVector!ubyte DER_encode() const
149     {
150         Vector!ubyte peer_cert_bits;
151         for (size_t i = 0; i != m_peer_certs.length; ++i)
152             peer_cert_bits ~= m_peer_certs[i].BER_encode();
153         
154         return DEREncoder()
155                 .startCons(ASN1Tag.SEQUENCE)
156                 .encode(cast(size_t)(TLS_SESSION_PARAM_STRUCT_VERSION))
157                 .encode(cast(size_t)(m_start_time.toUnixTime()))
158                 .encode(cast(size_t)(m_version.majorVersion()))
159                 .encode(cast(size_t)(m_version.minorVersion()))
160                 .encode(m_identifier, ASN1Tag.OCTET_STRING)
161                 .encode(m_session_ticket, ASN1Tag.OCTET_STRING)
162                 .encode(cast(size_t)(m_ciphersuite))
163                 .encode(cast(size_t)(m_compression_method))
164                 .encode(cast(size_t)(m_connection_side))
165                 .encode(cast(size_t)(m_fragment_size))
166 				.encode(m_extended_master_secret)
167                 .encode(m_master_secret, ASN1Tag.OCTET_STRING)
168                 .encode(m_orig_hs_hash, ASN1Tag.OCTET_STRING)
169                 .encode(peer_cert_bits, ASN1Tag.OCTET_STRING)
170                 .encode(ASN1String(m_server_info.hostname(), ASN1Tag.UTF8_STRING))
171                 .encode(ASN1String(m_server_info.service(), ASN1Tag.UTF8_STRING))
172                 .encode(cast(size_t)(m_server_info.port()))
173                 .encode(ASN1String(m_srp_identifier, ASN1Tag.UTF8_STRING))
174                 .endCons()
175                 .getContents();
176     }
177 
178     /**
179     * Encrypt a session (useful for serialization or session tickets)
180     */
181     Vector!ubyte encrypt(in SymmetricKey master_key, RandomNumberGenerator rng) const
182     {
183         const auto der = this.DER_encode();
184         
185         return CryptoBox.encrypt(der.ptr, der.length, master_key, rng);
186     }
187 
188     /**
189     * Decrypt a session created by encrypt
190     * Params:
191     *  buf = the ciphertext returned by encrypt
192     *  buf_len = the size of ctext in bytes
193     *  master_key = the same key used by the encrypting side
194     */
195     static TLSSession decrypt(const(ubyte)* buf, size_t buf_len, in SymmetricKey master_key)
196     {
197         try
198         {
199             const auto ber = CryptoBox.decrypt(buf, buf_len, master_key);
200             
201             return new TLSSession(ber.ptr, ber.length);
202         }
203         catch(Exception e)
204         {
205             throw new DecodingError("Failed to decrypt encrypted session -" ~  e.msg);
206         }
207     }
208 
209     /**
210     * Decrypt a session created by encrypt
211     * Params:
212     *  ctext = the ciphertext returned by encrypt
213     *  key = the same key used by the encrypting side
214     */
215     static TLSSession decrypt(const ref Vector!ubyte ctext, in SymmetricKey key)
216     {
217         return TLSSession.decrypt(ctext.ptr, ctext.length, key);
218     }
219 
220     /**
221     * Encode this session data for storage
222     * Notes: if the master secret is compromised so is the session traffic
223     */
224     string PEM_encode() const
225     {
226         return PEM.encode(this.DER_encode(), "SSL SESSION");
227     }
228 
229     /**
230     * Get the version of the saved session
231     */
232     TLSProtocolVersion Version() const { return m_version; }
233 
234     /**
235     * Get the ciphersuite code of the saved session
236     */
237     ushort ciphersuiteCode() const { return m_ciphersuite; }
238 
239     /**
240     * Get the ciphersuite info of the saved session
241     */
242     const(TLSCiphersuite) ciphersuite() const { return TLSCiphersuite.byId(m_ciphersuite); }
243 
244     /**
245     * Get the compression method used in the saved session
246     */
247     ubyte compressionMethod() const { return m_compression_method; }
248 
249     /**
250     * Get which side of the connection the resumed session we are/were
251     * acting as.
252     */
253     const(ConnectionSide) side() const { return m_connection_side; }
254 
255     /**
256     * Get the SRP identity (if sent by the client in the initial handshake)
257     */
258     string srpIdentifier() const { return m_srp_identifier; }
259 
260     /**
261     * Get the saved master secret
262     */
263     ref const(SecureVector!ubyte) masterSecret() const { return m_master_secret; }
264 
265     /**
266     * Get the session identifier
267     */
268     ref const(Vector!ubyte) sessionId() const { return m_identifier; }
269 
270     /**
271     * Get the negotiated maximum fragment size (or 0 if default)
272     */
273     size_t fragmentSize() const { return m_fragment_size; }
274 
275     /**
276     * Returns whether the session was negotiated with an extended master secret
277     */
278 	bool supportsExtendedMasterSecret() const { return m_extended_master_secret; }
279 
280     /**
281      * Get the original handshake hash (For ChannelID Resumption)
282      */
283     ref const(SecureVector!ubyte) originalHandshakeHash() const { return m_orig_hs_hash; }
284 
285     /**
286     * Return the certificate chain of the peer (possibly empty)
287     */
288     ref const(Vector!X509Certificate) peerCerts() const { return m_peer_certs; }
289 
290     /**
291     * Get the wall clock time this session began
292     */
293     SysTime startTime() const { return m_start_time; }
294 
295     /**
296     * Return how long this session has existed (in seconds)
297     */
298     const(Duration) sessionAge() const
299     {
300         return Clock.currTime(UTC()) - m_start_time;
301     }
302 
303     /**
304     * Return the session ticket the server gave us
305     */
306     ref const(Vector!ubyte) sessionTicket() const { return m_session_ticket; }
307 
308     TLSServerInformation serverInfo() const { return m_server_info; }
309 
310    /* @property TLSSession move() {
311         return TLSSession(m_identifier.move(), m_master_secret.move(), m_version, m_ciphersuite, m_compression_method, m_connection_side, 
312                           m_fragment_size, m_peer_certs.move(), m_session_ticket.move(), m_server_info, m_srp_identifier);
313     }*/
314 
315 private:
316 
317     enum { TLS_SESSION_PARAM_STRUCT_VERSION = 20160808 }
318 
319     SysTime m_start_time;
320 
321     Vector!ubyte m_identifier;
322     Vector!ubyte m_session_ticket; // only used by client side
323     SecureVector!ubyte m_master_secret;
324 
325     TLSProtocolVersion m_version;
326     ushort m_ciphersuite;
327     ubyte m_compression_method;
328     ConnectionSide m_connection_side;
329 
330     size_t m_fragment_size;
331 
332     Vector!X509Certificate m_peer_certs;
333     TLSServerInformation m_server_info; // optional
334     string m_srp_identifier; // optional
335     bool m_extended_master_secret;
336     SecureVector!ubyte m_orig_hs_hash;
337 }