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          TLSProtocolVersion _version,
46          ushort ciphersuite,
47          ubyte compression_method,
48          ConnectionSide side,
49          size_t fragment_size,
50          Vector!X509Certificate certs,
51          Vector!ubyte ticket,
52          in TLSServerInformation server_info,
53          in string srp_identifier)
54     {
55         m_start_time = Clock.currTime(UTC());
56         m_identifier = session_identifier.move();
57         m_session_ticket = ticket.move();
58         m_master_secret = master_secret.move();
59         m_version = _version;
60         m_ciphersuite = ciphersuite;
61         m_compression_method = compression_method;
62         m_connection_side = side;
63         m_fragment_size = fragment_size;
64         m_peer_certs = certs.move();
65         m_server_info = server_info;
66         m_srp_identifier = srp_identifier;
67     }
68 
69     /**
70     * Load a session from DER representation (created by DER_encode)
71     */
72     this(const(ubyte)* ber, size_t ber_len)
73     {
74         ubyte side_code = 0;
75         
76         ASN1String server_hostname;
77         ASN1String server_service;
78         size_t server_port;
79         
80         ASN1String srp_identifier_str;
81         
82         ubyte major_version = 0, minor_version = 0;
83         
84         Vector!ubyte peer_cert_bits;
85         
86         size_t start_time = 0;
87         
88         BERDecoder(ber, ber_len)
89                 .startCons(ASN1Tag.SEQUENCE)
90                 .decodeAndCheck(cast(size_t)(TLS_SESSION_PARAM_STRUCT_VERSION),
91                                   "Unknown version in session structure")
92                 .decodeIntegerType(start_time)
93                 .decodeIntegerType(major_version)
94                 .decodeIntegerType(minor_version)
95                 .decode(m_identifier, ASN1Tag.OCTET_STRING)
96                 .decode(m_session_ticket, ASN1Tag.OCTET_STRING)
97                 .decodeIntegerType(m_ciphersuite)
98                 .decodeIntegerType(m_compression_method)
99                 .decodeIntegerType(side_code)
100                 .decodeIntegerType(m_fragment_size)
101                 .decode(m_master_secret, ASN1Tag.OCTET_STRING)
102                 .decode(peer_cert_bits, ASN1Tag.OCTET_STRING)
103                 .decode(server_hostname)
104                 .decode(server_service)
105                 .decode(server_port)
106                 .decode(srp_identifier_str)
107                 .endCons()
108                 .verifyEnd();
109         
110         m_version = TLSProtocolVersion(major_version, minor_version);
111         m_start_time = SysTime(unixTimeToStdTime(cast(time_t)start_time));
112         m_connection_side = cast(ConnectionSide)(side_code);
113         
114         m_server_info = TLSServerInformation(server_hostname.value(),
115                                              server_service.value(),
116                                              cast(ushort) server_port);
117         
118         m_srp_identifier = srp_identifier_str.value();
119         
120         if (!peer_cert_bits.empty)
121         {
122             auto certs = DataSourceMemory(peer_cert_bits.ptr, peer_cert_bits.length);
123             while (!certs.endOfData())
124                 m_peer_certs.pushBack(X509Certificate(cast(DataSource)certs));
125         }
126     }
127 
128     /**
129     * Load a session from PEM representation (created by PEM_encode)
130     */
131     this(in string pem)
132     {
133         SecureVector!ubyte der = PEM.decodeCheckLabel(pem, "SSL SESSION");
134         
135         this(der.ptr, der.length);
136     }
137 
138     /**
139     * Encode this session data for storage
140     * Notes: if the master secret is compromised so is the session traffic
141     */
142     SecureVector!ubyte DER_encode() const
143     {
144         Vector!ubyte peer_cert_bits;
145         for (size_t i = 0; i != m_peer_certs.length; ++i)
146             peer_cert_bits ~= m_peer_certs[i].BER_encode();
147         
148         return DEREncoder()
149                 .startCons(ASN1Tag.SEQUENCE)
150                 .encode(cast(size_t)(TLS_SESSION_PARAM_STRUCT_VERSION))
151                 .encode(cast(size_t)(m_start_time.toUnixTime()))
152                 .encode(cast(size_t)(m_version.majorVersion()))
153                 .encode(cast(size_t)(m_version.minorVersion()))
154                 .encode(m_identifier, ASN1Tag.OCTET_STRING)
155                 .encode(m_session_ticket, ASN1Tag.OCTET_STRING)
156                 .encode(cast(size_t)(m_ciphersuite))
157                 .encode(cast(size_t)(m_compression_method))
158                 .encode(cast(size_t)(m_connection_side))
159                 .encode(cast(size_t)(m_fragment_size))
160                 .encode(m_master_secret, ASN1Tag.OCTET_STRING)
161                 .encode(peer_cert_bits, ASN1Tag.OCTET_STRING)
162                 .encode(ASN1String(m_server_info.hostname(), ASN1Tag.UTF8_STRING))
163                 .encode(ASN1String(m_server_info.service(), ASN1Tag.UTF8_STRING))
164                 .encode(cast(size_t)(m_server_info.port()))
165                 .encode(ASN1String(m_srp_identifier, ASN1Tag.UTF8_STRING))
166                 .endCons()
167                 .getContents();
168     }
169 
170     /**
171     * Encrypt a session (useful for serialization or session tickets)
172     */
173     Vector!ubyte encrypt(in SymmetricKey master_key, RandomNumberGenerator rng) const
174     {
175         const auto der = this.DER_encode();
176         
177         return CryptoBox.encrypt(der.ptr, der.length, master_key, rng);
178     }
179 
180     /**
181     * Decrypt a session created by encrypt
182     * Params:
183     *  buf = the ciphertext returned by encrypt
184     *  buf_len = the size of ctext in bytes
185     *  master_key = the same key used by the encrypting side
186     */
187     static TLSSession decrypt(const(ubyte)* buf, size_t buf_len, in SymmetricKey master_key)
188     {
189         try
190         {
191             const auto ber = CryptoBox.decrypt(buf, buf_len, master_key);
192             
193             return new TLSSession(ber.ptr, ber.length);
194         }
195         catch(Exception e)
196         {
197             throw new DecodingError("Failed to decrypt encrypted session -" ~  e.msg);
198         }
199     }
200 
201     /**
202     * Decrypt a session created by encrypt
203     * Params:
204     *  ctext = the ciphertext returned by encrypt
205     *  key = the same key used by the encrypting side
206     */
207     static TLSSession decrypt(const ref Vector!ubyte ctext, in SymmetricKey key)
208     {
209         return TLSSession.decrypt(ctext.ptr, ctext.length, key);
210     }
211 
212     /**
213     * Encode this session data for storage
214     * Notes: if the master secret is compromised so is the session traffic
215     */
216     string PEM_encode() const
217     {
218         return PEM.encode(this.DER_encode(), "SSL SESSION");
219     }
220 
221     /**
222     * Get the version of the saved session
223     */
224     TLSProtocolVersion Version() const { return m_version; }
225 
226     /**
227     * Get the ciphersuite code of the saved session
228     */
229     ushort ciphersuiteCode() const { return m_ciphersuite; }
230 
231     /**
232     * Get the ciphersuite info of the saved session
233     */
234     const(TLSCiphersuite) ciphersuite() const { return TLSCiphersuite.byId(m_ciphersuite); }
235 
236     /**
237     * Get the compression method used in the saved session
238     */
239     ubyte compressionMethod() const { return m_compression_method; }
240 
241     /**
242     * Get which side of the connection the resumed session we are/were
243     * acting as.
244     */
245     const(ConnectionSide) side() const { return m_connection_side; }
246 
247     /**
248     * Get the SRP identity (if sent by the client in the initial handshake)
249     */
250     string srpIdentifier() const { return m_srp_identifier; }
251 
252     /**
253     * Get the saved master secret
254     */
255     ref const(SecureVector!ubyte) masterSecret() const { return m_master_secret; }
256 
257     /**
258     * Get the session identifier
259     */
260     ref const(Vector!ubyte) sessionId() const { return m_identifier; }
261 
262     /**
263     * Get the negotiated maximum fragment size (or 0 if default)
264     */
265     size_t fragmentSize() const { return m_fragment_size; }
266 
267     /**
268     * Return the certificate chain of the peer (possibly empty)
269     */
270     ref const(Vector!X509Certificate) peerCerts() const { return m_peer_certs; }
271 
272     /**
273     * Get the wall clock time this session began
274     */
275     SysTime startTime() const { return m_start_time; }
276 
277     /**
278     * Return how long this session has existed (in seconds)
279     */
280     const(Duration) sessionAge() const
281     {
282         return Clock.currTime(UTC()) - m_start_time;
283     }
284 
285     /**
286     * Return the session ticket the server gave us
287     */
288     ref const(Vector!ubyte) sessionTicket() const { return m_session_ticket; }
289 
290     TLSServerInformation serverInfo() const { return m_server_info; }
291 
292    /* @property TLSSession move() {
293         return TLSSession(m_identifier.move(), m_master_secret.move(), m_version, m_ciphersuite, m_compression_method, m_connection_side, 
294                           m_fragment_size, m_peer_certs.move(), m_session_ticket.move(), m_server_info, m_srp_identifier);
295     }*/
296 
297 private:
298 
299     enum { TLS_SESSION_PARAM_STRUCT_VERSION = 0x2994e301 }
300 
301     SysTime m_start_time;
302 
303     Vector!ubyte m_identifier;
304     Vector!ubyte m_session_ticket; // only used by client side
305     SecureVector!ubyte m_master_secret;
306 
307     TLSProtocolVersion m_version;
308     ushort m_ciphersuite;
309     ubyte m_compression_method;
310     ConnectionSide m_connection_side;
311 
312     size_t m_fragment_size;
313 
314     Vector!X509Certificate m_peer_certs;
315     TLSServerInformation m_server_info; // optional
316     string m_srp_identifier; // optional
317 }