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 }