diff options
Diffstat (limited to 'lib/ssl')
55 files changed, 9450 insertions, 7143 deletions
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 2ef5e331a8..301ff21068 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1999</year><year>2012</year> + <year>1999</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -22,24 +22,224 @@ </legalnotice> <title>SSL Release Notes</title> - <prepared>Peter Högfeldt</prepared> - <docno></docno> - <date>2003-08-03</date> - <rev>G</rev> <file>notes.xml</file> </header> <p>This document describes the changes made to the SSL application.</p> - - <section><title>SSL 5.1.2.1</title> + <section><title>SSL 5.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Honor the versions option to ssl:connect and ssl:listen.</p> + <p> + Own Id: OTP-10905</p> + </item> + <item> + <p> + Next protocol negotiation with reused sessions will now + succeed</p> + <p> + Own Id: OTP-10909</p> + </item> + </list> + </section> + <section><title>Improvements and New Features</title> <list> <item> <p> - Make log_alert configurable as option in ssl, SSLLogLevel - added as option to inets conf file</p> + Add support for PSK (Pre Shared Key) and SRP (Secure + Remote Password) chipher suits, thanks to Andreas + Schultz.</p> + <p> + Own Id: OTP-10450 Aux Id: kunagi-269 [180] </p> + </item> + <item> + <p> + Fix SSL Next Protocol Negotiation documentation. Thanks + to Julien Barbot.</p> + <p> + Own Id: OTP-10955</p> + </item> + <item> + <p> + Fix ssl_connection to support reading proxy/chain + certificates. Thanks to Valentin Kuznetsov.</p> + <p> + Own Id: OTP-10980</p> + </item> + <item> + <p> + Integrate elliptic curve contribution from Andreas + Schultz </p> + <p> + In order to be able to support elliptic curve cipher + suites in SSL/TLS, additions to handle elliptic curve + infrastructure has been added to public_key and crypto.</p> + <p> + This also has resulted in a rewrite of the crypto API to + gain consistency and remove unnecessary overhead. All OTP + applications using crypto has been updated to use the new + API.</p> + <p> + Impact: Elliptic curve cryptography (ECC) offers + equivalent security with smaller key sizes than other + public key algorithms. Smaller key sizes result in + savings for power, memory, bandwidth, and computational + cost that make ECC especially attractive for constrained + environments.</p> + <p> + Own Id: OTP-11009</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 5.2.1</title> + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Transport callback handling is changed so that gen_tcp is + treated as a special case where inet will be called + directly for functions such as setopts, as gen_tcp does + not have its own setopts. This will enable users to use + the transport callback for other customizations such as + websockets.</p> + <p> + Own Id: OTP-10847</p> + </item> + <item> + <p> + Follow up to OTP-10451 solved in ssl-5.2 R16A. Make sure + format_error return good strings. Replace confusing + legacy atoms with more descriptive atoms.</p> + <p> + Own Id: OTP-10864</p> + </item> + </list> + </section> + +</section> +<section><title>SSL 5.1.2.1</title> +<section><title>Improvements and New Features</title> +<list> + <item> + <p> + Make log_alert configurable as option in ssl, SSLLogLevel + added as option to inets conf file</p> + <p> + Own Id: OTP-11259</p> + </item> +</list> +</section> +</section> +<section><title>SSL 5.2</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + SSL: TLS 1.2, advertise sha224 support, thanks to Andreas + Schultz.</p> + <p> + Own Id: OTP-10586</p> + </item> + <item> + <p> + If an ssl server is restarted with new options and a + client tries to reuse a session the server must make sure + that it complies to the new options before agreeing to + reuse it.</p> + <p> + Own Id: OTP-10595</p> + </item> + <item> + <p> + Now handles cleaning of CA-certificate database correctly + so that there will be no memory leek, bug was introduced + in ssl- 5.1 when changing implementation to increase + parallel execution.</p> + <p> + Impact: Improved memory usage, especially if you have + many different certificates and upgrade tcp-connections + to TLS-connections.</p> + <p> + Own Id: OTP-10710</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Support Next Protocol Negotiation in TLS, thanks to Ben + Murphy for the contribution.</p> + <p> + Impact: Could give performance benefit if used as it + saves a round trip.</p> + <p> + Own Id: OTP-10361 Aux Id: kunagi-214 [125] </p> + </item> + <item> + <p> + TLS 1.2 will now be the default TLS version if sufficient + crypto support is available otherwise TLS 1.1 will be + default.</p> + <p> + Impact: A default TLS connection will have higher + security and hence it may be perceived as slower then + before.</p> + <p> + Own Id: OTP-10425 Aux Id: kunagi-275 [186] </p> + </item> + <item> + <p> + It is now possible to call controlling_process on a + listen socket, same as in gen_tcp.</p> + <p> + Own Id: OTP-10447</p> + </item> + <item> + <p> + Remove filter mechanisms that made error messages + backwards compatible with old ssl but hid information + about what actually happened.</p> + <p> + This does not break the documented API however other + reason terms may be returned, so code that matches on the + reason part of {error, Reason} may fail.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-10451 Aux Id: kunagi-270 [181] </p> + </item> + <item> + <p> + Added missing dependencies to Makefile</p> <p> - Own Id: OTP-11259</p> + Own Id: OTP-10594</p> + </item> + <item> + <p> + Removed deprecated function ssl:pid/0, it has been + pointless since R14 but has been keep for backwards + compatibility.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-10613 Aux Id: kunagi-331 [242] </p> + </item> + <item> + <p> + Refactor to simplify addition of key exchange methods, + thanks to Andreas Schultz.</p> + <p> + Own Id: OTP-10709</p> </item> </list> </section> @@ -621,1285 +821,7 @@ </item> </list> </section> - - </section> - - - <section><title>SSL 3.11.1</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Fixed handling of several ssl/tls packets arriving at the - same time. This was broken during a refactoring of the - code.</p> - <p> - Own Id: OTP-8679</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Added missing checks for padding and Mac value. Removed - code for export ciphers and DH certificates as we decided - not to support them.</p> - <p> - Own Id: OTP-7047</p> - </item> - <item> - <p> - New ssl will no longer return esslerrssl to be backwards - compatible with old ssl as this hids infomation from the - user. format_error/1 has been updated to support new ssl.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-7049</p> - </item> - <item> - <p> - New ssl now supports secure renegotiation as described by - RFC 5746.</p> - <p> - Own Id: OTP-8568</p> - </item> - <item> - <p> - Alert handling has been improved to better handle - unexpected but valid messages and the implementation is - also changed to avoid timing related issues that could - cause different error messages depending on network - latency. Packet handling was sort of broken but would - mostly work as expected when socket was in binary mode. - This has now been fixed.</p> - <p> - Own Id: OTP-8588</p> - </item> - </list> - </section> - -</section> - -<section><title>SSL 3.11</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Fixes handling of the option fail_if_no_peer_cert and - some undocumented options. Thanks to Rory Byrne.</p> - <p> - Own Id: OTP-8557</p> - </item> - </list> - </section> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Support for Diffie-Hellman. ssl-3.11 requires - public_key-0.6.</p> - <p> - Own Id: OTP-7046</p> - </item> - <item> - <p> - New ssl now properly handles ssl renegotiation, and - initiates a renegotiation if ssl/ltls-sequence numbers - comes close to the max value. However RFC-5746 is not yet - supported, but will be in an upcoming release.</p> - <p> - Own Id: OTP-8517</p> - </item> - <item> - <p> - When gen_tcp is configured with the {packet,http} option, - it automatically switches to expect HTTP Headers after a - HTTP Request/Response line has been received. This update - fixes ssl to behave in the same way. Thanks to Rory - Byrne.</p> - <p> - Own Id: OTP-8545</p> - </item> - <item> - <p> - Ssl now correctly verifies the extended_key_usage - extension and also allows the user to verify application - specific extensions by supplying an appropriate fun.</p> - <p> - Own Id: OTP-8554 Aux Id: OTP-8553 </p> - </item> - <item> - <p> - Fixed ssl:transport_accept/2 to return properly when - socket is closed. Thanks to Rory Byrne.</p> - <p> - Own Id: OTP-8560</p> - </item> - </list> </section> - -</section> - -<section><title>SSL 3.10.9</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Fixed a crash in the certificate certification part.</p> - <p> - Own Id: OTP-8510 Aux Id: seq11525 </p> - </item> - </list> - </section> - -</section> - -<section><title>SSL 3.10.8</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p><c>ssl:send/2</c> ignored packet option, fix provided - by YAMASHINA Hio.</p> - <p>Fixed a file cache bug which caused problems when the - same file was used for both cert and cacert.</p> - <p>Allow <c>ssl:listen/2</c> to be called with option - {ssl_imp, old}.</p> - <p> Fixed ssl:setopts(Socket, binary) which didn't work - for 'new' ssl.</p>. - <p> - Own Id: OTP-8441</p> - </item> - <item> - <p> - Do a controlled shutdown if a non ssl packet arrives as - the first packet.</p> - <p> - Own Id: OTP-8459 Aux Id: seq11505 </p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p>Fixed session reuse (in new_ssl), thanks Wil Tan.</p> - <p>Send CA list during Certificate Request (in new_ssl) , - thanks Wil Tan.</p> <p><c>NOTE</c>: SSL (new_ssl) - requires public_key-0.5.</p> - <p> - Own Id: OTP-8372</p> - </item> - </list> - </section> - -</section> - -<section><title>SSL 3.10.7</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - A ticker process could potentially be blocked - indefinitely trying to send a tick to a node not - responding. If this happened, the connection would not be - brought down as it should.</p> - <p> This requires erts-5.7.4 and kernel-2.13.4 or later - to be able to get the erlang distribution over ssl to work.</p> - <p> - Own Id: OTP-8218</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - The documentation is now built with open source tools - (xsltproc and fop) that exists on most platforms. One - visible change is that the frames are removed.</p> - <p> - Own Id: OTP-8250</p> - </item> - <item> - <p> - Code cleanup from Kostis.</p> - <p> - Own Id: OTP-8260</p> - </item> - </list> - </section> - -</section> - -<section><title>SSL 3.10.6</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - The ssl:ssl_accept/3 issue was not properly fixed in the - previous patch, see OTP-8244.</p> - <p> - Own Id: OTP-8275 Aux Id: seq11451 </p> - </item> - </list> - </section> - -</section> - -<section><title>SSL 3.10.5</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Allow clients to not send certificates if option - <c>fail_if_no_peer_cert</c> was not set.</p> - <p> - Own Id: OTP-8224</p> - </item> - <item> - <p>An ssl:ssl_accept/3 could crash a connection if the - timing was wrong.</p> <p>Removed info message if the - socket closed without a proper disconnect from the ssl - layer. </p> <p>ssl:send/2 is now blocking until the - message is sent.</p> - <p> - Own Id: OTP-8244 Aux Id: seq11420 </p> - </item> - </list> - </section> - -</section> - -<section><title>SSL 3.10.4</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - A client could avoid a certificate check if the client - code didn't send the requested certificate.</p> - <p> - Own Id: OTP-8137</p> - </item> - </list> - </section> - -</section> - -<section><title>SSL 3.10.3</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p>Packet handling was not implemented correctly.</p> - <p>Inet option handling support have been improved.</p> - <p>The <c>verify_fun</c> is now invoked even if - verify_peer is used, that implies that by default - {bad_cert,unknown_ca} is an accepted fault during the - client connection phase. The check can still be done by - suppling another verify_fun.</p> - <p> - Own Id: OTP-8011 Aux Id: seq11287 </p> - </item> - </list> - </section> - -</section> - - -<section><title>SSL 3.10.2</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - A "new_ssl" socket was not closed if the controlling - process died without calling ssl:close/1.</p> - <p> - Own Id: OTP-7963 Aux Id: seq11276 </p> - </item> - </list> - </section> - -</section> - -<section><title>SSL 3.10.1</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Fixed bug that caused the ssl handshake finished message - to be calculated wrongly under the circumstances that the - server did not send the trusted cert and that the - previous cert did not have the extension telling us the - trusted certs name. This manifested it self as - bad_record_mac alert from the server.</p> - <p> - Own Id: OTP-7878</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - The cacertsfile option is now optional for ssl servers.</p> - <p> - Own Id: OTP-7656</p> - </item> - <item> - <p> - For the ssl client the options cacertfile, certfile and - keyfile are now optional as they are not always needed - depending on configuration of the client itself and the - configuration of the server. Also as PEM-files may - contain more than one entry the keyfile option will - default to the same file as given by the certfile option.</p> - <p> - Own Id: OTP-7870</p> - </item> - <item> - <p> - Added new ssl client option verify_fun.</p> - <p> - Own Id: OTP-7871</p> - </item> - </list> - </section> - -</section> - - <section><title>SSL 3.10</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Error log entries are now formatted correctly.</p> - <p> - Own Id: OTP-7258</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - All handling of X509-certificates and public keys have - been moved to the new application public_key.</p> - <p> - Own Id: OTP-6894</p> - </item> - <item> - <p> - New ssl now supports SSL-3.0 and TLS-1.0</p> - <p> - Own Id: OTP-7037</p> - </item> - <item> - <p> - New ssl now supports all inet-packet types.</p> - <p> - Own Id: OTP-7039</p> - </item> - <item> - <p> - The new ssl-server is now able to send a certificate - request to the client. However new options may be - introduced later to fully support all features regarding - certificate requests.</p> - <p> - Own Id: OTP-7150</p> - </item> - </list> - </section> - - - <section><title>Known Bugs and Problems</title> - <list> - <item> - <p> - Running erlang distribution over ssl don't work as - described in the documentation.</p> - <p> - Own Id: OTP-7536</p> - </item> - </list> - </section> - - </section> - - - <section><title>SSL 3.9</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - ssl_prim.erl was passing an FD rather than an #sslsocket - to ssl_broker:ssl_accept_prim. This could cause problems - in the deprecated accept function, this will not cause - any more problems however this function is deprecated!</p> - <p> - Own Id: OTP-6926</p> - </item> - <item> - <p> - Erlang distribution over ssl was broken after R11B-0, - this has now been fixed.</p> - <p> - Own Id: OTP-7004</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - All inet options are available in the new ssl - implementation that is released as a alfa in ssl-3.9 and - will replace the old implementation in ssl-4.0. This will - not be fixed in the old implementation.</p> - <p> - Own Id: OTP-4677</p> - </item> - <item> - <p> - The new ssl implementation released as a alfa in this - version supports upgrading of a tcp connection to an ssl - connection so that http client and servers may implement - RFC 2817.</p> - <p> - Own Id: OTP-5510</p> - </item> - <item> - <p>A new implementation of ssl is released as a alfa - version in ssl-3.9 it will later replace the old - implementation in ssl-4.0. The new implementation can be - accessed by providing the option {ssl_imp, new} to the - ssl:connect and ssl:listen functions.</p> - <p>The new implementation is Erlang based and all logic - is in Erlang and only payload encryption calculations are - done in C via the crypto application. The main reason for - making a new implementation is that the old solution was - very crippled as the control of the ssl-socket was deep - down in openssl making it hard if not impossible to - support all inet options, ipv6 and upgrade of a tcp - connection to an ssl connection. The alfa version has a - few limitations that will be removed before the ssl-4.0 - release. Main differences and limitations in the alfa are - listed below.</p> - - <list type="bulleted"> <item>New ssl requires the crypto - application.</item> <item>The option reuseaddr is - supported and the default value is false as in gen_tcp. - Old ssl is patched to accept that the option is set to - true to provide a smoother migration between the - versions. In old ssl the option is hard coded to - true.</item> <item>ssl:version/0 is replaced by - ssl:versions/0</item> <item>ssl:ciphers/0 is replaced by - ssl:cipher_suites/0</item> <item>ssl:pid/1 is a - meaningless function in new ssl and will be deprecated in - ssl-4.0 until it is removed it will return a valid but - meaningless pid.</item> <item>New API functions are - ssl:shutdown/2, ssl:cipher_suites/[0,1] and - ssl:versions/0</item> <item>Diffie-Hellman keyexchange is - not supported.</item> <item>Not all inet packet types are - supported.</item> <item>CRL and policy certificate - extensions are not supported.</item> <item>In this alfa - only sslv3 is enabled, although tlsv1 and tlsv1.1 - versions are implemented and will be supported in future - versions.</item> <item>For security reasons sslv2 is not - supported.</item> </list> - <p> - Own Id: OTP-6619</p> - </item> - <item> - <p> - New ssl implementation, released as alfa in ssl-3.9, - supports ipv6. It will not be supported in the old - implementation.</p> - <p> - Own Id: OTP-6637 Aux Id: OTP-6636 </p> - </item> - </list> - </section> - - </section> - - <section> - <title>SSL 3.1.1.1</title> - - <section> - <title>Minor Makefile changes</title> - <list type="bulleted"> - <item> - <p>Removed use of <c>erl_flags</c> from Makefile.</p> - <p>Own Id: OTP-6689</p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 3.1.1</title> - - <section> - <title>Crash on error in ssl_accept</title> - <list type="bulleted"> - <item> - <p>A bug in ssl_accept could cause all ssl - connections to hang when a connection - attempt was closed by the client while - the server was in <c>ssl_accept</c>.</p> - <p>Own Id: OTP-6612 Aux Id: seq10599</p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 3.1</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>SSL now uses a two-phase accept, with a separate accept - calls for the socket and the ssl protocol. This avoids - timeouts when a client doesn't initiate ssl handshake.</p> - <p>With the old implementation of accept, the server - was locked by a client, if the client didn't do - proper ssl handshake.</p> - <p>Own Id: OTP-6418 Aux Id: seq10105</p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 3.0.12</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>An integer array pointing to a struct pollfd array, is - now reset before file descriptors are collected to be - included in a call to poll(). This is to prevent file - descriptors to be mixed up.</p> - <p>Own Id: OTP-6084</p> - </item> - <item> - <p>The generation of the module ssl_pkix_oid contained - multiple identifiers, which made the mapping between - atoms and identifiers not one-to-one.</p> - <p>Own Id: OTP-6085</p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 3.0.11</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>The state of a connection in active mode could be in a - restrictive state, so that an internal tcp_closed message - was incorrectly considered illegal, resulting in a - premature termination of the connection process.</p> - <p>Own Id: OTP-5972 Aux Id: seq10188 </p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 3.0.10</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>Erlang distribution over SSL was broken. Corrected. - (Thanks to Fredrik Thulin.)</p> - <p>Own Id: OTP-5863</p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 3.0.9</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>The port program for the ssl application could waste huge - amounts of CPU time if a write could not be completed - directly and was put in the write queue. (Only on platforms - where poll() is used, such as Solaris and Linux.)</p> - <p>Own Id: OTP-5784</p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 3.0.8</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>A process reading only a portion of a sufficiently large - amount of data from an accepted socket, and then quering - the ssl library (e.g. ssl:getpeername()), would cause a - global deadlock in the esock port program.</p> - <p>Own Id: OTP-5702</p> - </item> - <item> - <p>A spelling error in the module <c>ssl_pkix</c> caused the - call to <c>ssl:peercert/2</c> to fail when the option - <c>subject</c> was used.</p> - <p>Own Id: OTP-5708</p> - </item> - <item> - <p>Because fopen() on Solaris 8 can't handle file - descriptor numbers above 255, reading of certificate - files would fail if all file descriptors below 256 were - in use (typically, if many connections were open). This - problem has been worked around.</p> - <p>The ssl application's port program used to use - select(), which meant that it could not handle more than - FD_SETSIZE file descriptors (usually 1024). To eliminate - that limitation, poll() is now used on all platforms that - support it.</p> - <p>Solaris/Sparc, 64-bit emulator: The SO_REUSEADDR - option was not set for listen sockets, which essentially - made the ssl application unusable. Corrected.</p> - <p>The default listen queue size for ssl port program was - changed to 128 (from 5).</p> - <p>Own Id: OTP-5755 Aux Id: seq10068 </p> - </item> - </list> - </section> - </section> - - <section> - <title>Ssl 3.0.7</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>The R/W buffer length i esock.c was too small. It has - been increased from 4k to 32k.</p> - <p>Own Id: OTP-5620</p> - </item> - </list> - </section> - </section> - - <section> - <title>Ssl 3.0.6</title> - - <section> - <title>Improvements and New Features</title> - <list type="bulleted"> - <item> - <p>A configuration option for choosing protocol versions has - been added (<c>sslv2</c>, <c>sslv3</c>, and - <c>tlsv1</c>).</p> - <p>Own Id: OTP-5429 Aux Id: seq9755 </p> - </item> - </list> - </section> - </section> - - <section> - <title>Ssl 3.0.5</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>Linked in drivers in the crypto, and asn1 applications - are now compiled with the -D_THREAD_SAFE and -D_REENTRANT - switches on unix when the emulator has thread support - enabled.</p> - <p>Linked in drivers on MacOSX are not compiled with the - undocumented -lbundle1.o switch anymore. Thanks to Sean - Hinde who sent us a patch.</p> - <p>Linked in driver in crypto, and port programs in ssl, now - compiles on OSF1.</p> - <p>Minor makefile improvements in runtime_tools.</p> - <p>Own Id: OTP-5346</p> - </item> - </list> - </section> - </section> - - <section> - <title>Ssl 3.0.4</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p><c>ssl:recv/3</c> with finite timeout value, closed the - connection at timeout.</p> - <p>Own Id: OTP-4882</p> - </item> - </list> - </section> - </section> - - <section> - <title>Ssl 3.0.3</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>When a file descriptor was marked for closing, and - end-of-file condition had already been detected, the file - descriptor was never closed.</p> - <p>Own Id: OTP-5093 Aux Id: seq8806 </p> - </item> - <item> - <p>When the number of open file descriptors reached - FD_SETSIZE, the SSL port program entered a busy loop.</p> - <p>Own Id: OTP-5094 Aux Id: seq8806 </p> - </item> - </list> - </section> - - <section> - <title>Improvements and New Features</title> - <list type="bulleted"> - <item> - <p>The SSL application now supports SSL sessions for - servers, which typically speeds up HTTP requests from - browsers.</p> - <p>Own Id: OTP-5095</p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 3.0.2</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>The UTF8String type is now defined in asn1-1.4.4.2 and - later. Therefore the definitions of UTF8String has been - removed from the ASN.1 modules PKIX1Explicit88.asn1 and - PKIXAttributeCertificate.asn1. The SSL application can now - only be built using asn-1.4.4.2 or later.</p> - <p>OwnId: OTP-4971.</p> - </item> - </list> - </section> - - <section> - <title>Known Bugs and Problems</title> - <p>See SSL-3.0. - </p> - </section> - </section> - - <section> - <title>SSL 3.0.1</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>An unexpected object identifier would crash <c>ssl:peercert</c>. </p> - <p>OwnId: OTP-4771.</p> - </item> - </list> - </section> - - <section> - <title>Known Bugs and Problems</title> - <p>See SSL-3.0. - </p> - </section> - </section> - - <section> - <title>SSL 3.0</title> - - <section> - <title>Improvements and New Features</title> - <list type="bulleted"> - <item> - <p>The <c>cache_timout</c> option was silently ignored. It had - to do with SSL sessions, where multiple connections can occur. - Since the Erlang SSL application does not support sessions the - option is still ignored, and consequently the documentation - about it has been removed.</p> - <p>OwnId: OTP-3146</p> - </item> - <item> - <p>The Erlang SSL application is now based on OpenSSL version - 0.9.7a. OpenSSL 0.9.6 should also work.</p> - <p>OwnId: OTP-4002</p> - </item> - <item> - <p>When connecting it is now possible to bind to a local address - and local port. </p> - <p>OwnId: OTP-4675</p> - </item> - <item> - <p>The <c>ssl_esock</c> port program is now part of the - distribution and thus does not have to be created - explicitly. It is dynamically linked to OpenSSL - libraries in a "standard" location (typically - <c>/usr/local/lib</c> on UNIX; in the path on Win32).</p> - <p>OwnId: - OTP-4676</p> - </item> - <item> - <p>The new functions <c>ssl:peercert/1/2</c> provide information - from the certificate of a peer of a connection.</p> - <p>OwnId: OTP-4680 - <br></br> -Aux Id: seq7688</p> - </item> - <item> - <p>The function <c>ssl:port/1</c> has been removed from the - documentation, but not from the <c>ssl</c> interface module. - The recommendation is to use <c>ssl:peername/1</c> - instead, which provides both address and port of the peer.</p> - <p>OwnId: OTP-4681 </p> - </item> - <item> - <p>New User's Guide documentation has been added.</p> - <p>OwnId: OTP-4682 </p> - </item> - <item> - <p>The old <c>ssl_socket</c> interface has been removed and also - the documentation of it. </p> - <p>OwnId: OTP-4683 </p> - </item> - <item> - <p>The use of ephemeral RSA keys is now supported. It is - a global configuration option (see the ssl(6) manual page).</p> - <p>OwnId: OTP-4691.</p> - </item> - </list> - </section> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>The option <c>cacertfile</c> is now in effect, and can - therefore no longer be set with the OS environment - variable SSL_CERT_FILE (which did set the same value for - all connections). </p> - <p>OwnId: OTP-3146</p> - </item> - <item> - <p>There was a synchronization error at closing of an SSL - connection. </p> - <p>OwnId: OTP-4435 - <br></br> -Aux Id: seq7534</p> - </item> - <item> - <p>C macros in <c>debuglog.c</c> were not ANSI C compliant.</p> - <p>OwnId: OTP-4674</p> - </item> - <item> - <p>The <c>binary</c> option was not properly handled.</p> - <p>OwnId: OTP-4678</p> - </item> - <item> - <p>The <c>ssl:format_error/1</c> did not consider <c>inet</c> - error codes, nor did it have a catch all for unknown error - codes.</p> - <p>OwnId: OTP-4679</p> - </item> - </list> - </section> - - <section> - <title>Known Bugs and Problems</title> - <list type="bulleted"> - <item> - <p>Change of controlling process in not OTP compliant. </p> - <p>OwnId; OTP-4712</p> - </item> - <item> - <p>There is still no way to restrict the cipher sizes. </p> - <p>OwnId: OTP-4712</p> - </item> - <item> - <p>The <c>keep_alive</c> and <c>reuse_addr</c> options will be - added in a future release. </p> - <p>OwnId: OTP-4677</p> - </item> - <item> - <p>There is currently no way to restrict the SSL/TLS - protocol versions to use. In a future release this will be - supported as a configuration option, and as an option for - each connection as well. </p> - <p>OwnId: OTP-4711.</p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 2.3.6</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>There was a synchronization error at closing, which could - result in that an SSL socket was removed prematurely, resulting - in that a user process referring to it received an unexpected - exit.</p> - <p>OwnId: OTP-4435 - <br></br> -Aux Id: seq7600</p> - </item> - </list> - </section> - - <section> - <title>Known Bugs and Problems</title> - <p>See SSL 2.2 . </p> - </section> - </section> - - <section> - <title>SSL 2.3.5</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>Setting of the option `nodelay' caused the SSL port program - to dump core.</p> - <p>OwnId: OTP-4380 - <br></br> -Aux Id: -</p> - </item> - <item> - <p>Setting of the option '{active, once}' in <c>setopts</c> was - wrong, causing a correct socket message to be regarded as - erroneous. </p> - <p>OwnId: OTP-4380 - <br></br> -Aux Id: -</p> - </item> - <item> - <p>A self-signed peer certificate was always rejected with the - error `eselfsignedcert', irrespective of the `depth' value. </p> - <p>OwnId: OTP-4374 - <br></br> -Aux Id: seq7417</p> - </item> - </list> - </section> - - <section> - <title>Known Bugs and Problems</title> - <p>See SSL 2.2 . </p> - </section> - </section> - - <section> - <title>SSL 2.3.4</title> - - <section> - <title>Improvements and New Features</title> - <list type="bulleted"> - <item> - <p>All TCP options allowed in gen_tcp, are now also allowed in - SSL, except the option <c>{reuseaddr, Boolean}</c>. A new - function <c>getopts</c> has been added to the SSL interface - module <c>ssl</c>. </p> - <p>OwnId: OTP-4305, OTP-4159</p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 2.3.3</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>The roles of the SSLeay and OpenSSL packages has been - clarified in the ssl(6) application manual page. Also - the URLs from which to download SSLeay has been updated.</p> - <p>OwnId: OTP-4002 - <br></br> -Aux Id: seq5269</p> - </item> - <item> - <p>A call to <c>ssl:listen(Port, Options)</c> with - <c>Options = []</c> resulted in the cryptic <c>{error, ebadf}</c> return value. The return value has been changed - to <c>{error, enooptions}</c>, and the behaviour has been - documented in the <c>listen/2</c> function.</p> - <p>OwnId: OTP-4016 - <br></br> -Aux Id: seq7006</p> - </item> - <item> - <p>Use of the option <c>{nodelay, boolean()}</c> crashed - the <c>ssl_server</c>.</p> - <p>OwnId: OTP-4070 - <br></br> -Aux Id:</p> - </item> - <item> - <p>A bug caused the Erlang distribution over ssl to fail. - This bug has now been fixed.</p> - <p>OwnId: OTP-4072 - <br></br> -Aux Id:</p> - </item> - <item> - <p>On Windows when the SSL port program encountered an - error code not anticipated it crashed. </p> - <p>OwnId: OTP-4132 - <br></br> -Aux Id:</p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 2.3.2</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>The <c>ssl:accept/1-2</c> function sometimes returned - <c>{error, {What, Where}}</c> instead of <c>{error, What}</c>, where <c>What</c> is an atom. </p> - <p>OwnId: OTP-3775 - <br></br> -Aux Id: seq4991</p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 2.3.1</title> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>Sometimes the SSL portprogram would loop in an accept - loop, without terminating even when the SSL application - was stopped.. </p> - <p>OwnId: OTP-3691</p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 2.3</title> - <p>Functions have been added to SSL to experimentally support - Erlang distribution. - </p> - </section> - - <section> - <title>SSL 2.2.1</title> - <p>The 2.2.1 version of SSL provides code replacement in runtime - by upgrading from, or downgrading to, versions 2.1 and 2.2. - </p> - </section> - - <section> - <title>SSL 2.2</title> - - <section> - <title>Improvements and New Features</title> - <list type="bulleted"> - <item> - <p>The restriction that only the creator of an SSL socket can - read from and write to the socket has been lifted.</p> - <p>OwnId: OTP-3301</p> - </item> - <item> - <p>The option <c>{packet, cdr}</c> for SSL sockets has been added, - which means that SSL sockets also supports CDR encoded packets.</p> - <p>OwnId: OTP-3302</p> - </item> - </list> - </section> - - <section> - <title>Known Bugs and Problems</title> - <list type="bulleted"> - <item> - <p>Setting of a CA certificate file with the <c>cacertfile</c> - option (in calls to <c>ssl:accept/1/2</c> or - <c>ssl:connect/3/4</c>) does not work due to weaknesses - in the SSLeay package. </p> - <p>A work-around is to set the OS environment variable - <c>SSL_CERT_FILE</c> before SSL is started. However, then - the CA certificate file will be global for all connections.</p> - <p>OwnId: OTP-3146</p> - </item> - <item> - <p>When changing controlling process of an SSL socket, a - temporary process is started, which is not gen_server - compliant.</p> - <p>OwnId: OTP-3146</p> - </item> - <item> - <p>Although there is a <c>cache</c> timeout option, it is - silently ignored.</p> - <p>OwnId: OTP-3146</p> - </item> - <item> - <p>There is currently no way to restrict the cipher sizes.</p> - <p>OwnId: OTP-3146</p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 2.1</title> - - <section> - <title>Improvements and New Features</title> - <list type="bulleted"> - <item> - <p>The set of possible error reasons has been extended to - contain diagnostics on erroneous certificates and failures - to verify certificates.</p> - <p>OwnId: OTP-3145</p> - </item> - <item> - <p>The maximum number of simultaneous SSL connections on - Windows has been increased from 31 to 127.</p> - <p>OwnId: OTP-3145</p> - </item> - </list> - </section> - - <section> - <title>Fixed Bugs and Malfunctions</title> - <list type="bulleted"> - <item> - <p>A dead-lock occurring when write queues are not empty has - been removed. </p> - <p>OwnId: OTP-3145</p> - </item> - <item> - <p>Error reasons have been unified and changed.</p> - <p>(** POTENTIAL INCOMPATIBILITY **)</p> - <p>OwnId: OTP-3145</p> - </item> - <item> - <p>On Windows a check of the existence of the environment - variable <c>ERLSRV_SERVICE_NAME</c> has been added. If - that variable is defined, the port program of the SSL - application will not terminated when a user logs off.</p> - <p>OwnId: OTP-3145</p> - </item> - <item> - <p>An error in the setting of the <c>nodelay</c> option - has been corrected.</p> - <p>OwnId: OTP-3145</p> - </item> - <item> - <p>The confounded notions of verify mode and verify depth has - been corrected. The option <c>verifydepth</c> has been - removed, and the two separate options <c>verify</c> and - <c>depth</c> has been added.</p> - <p>(** POTENTIAL INCOMPATIBILITY **)</p> - <p>OwnId: OTP-3145</p> - </item> - </list> - </section> - - <section> - <title>Known Bugs and Problems</title> - <list type="bulleted"> - <item> - <p>Setting of a CA certificate file with the <c>cacertfile</c> - option (in calls to <c>ssl:accept/1/2</c> or - <c>ssl:connect/3/4</c>) does not work due to weaknesses - in the SSLeay package. </p> - <p>A work-around is to set the OS environment variable - <c>SSL_CERT_FILE</c> before SSL is started. However, then - the CA certificate file will be global for all connections.</p> - <p>OwnId: OTP-3146</p> - </item> - <item> - <p>When changing controlling process of an SSL socket, a - temporary process is started, which is not gen_server - compliant.</p> - <p>OwnId: OTP-3146</p> - </item> - <item> - <p>Although there is a <c>cache</c> timeout option, it is - silently ignored.</p> - <p>OwnId: OTP-3146</p> - </item> - <item> - <p>There is currently no way to restrict the cipher sizes.</p> - <p>OwnId: OTP-3146</p> - </item> - </list> - </section> - </section> - - <section> - <title>SSL 2.0</title> - <p>A complete new version of SSL with separate I/O channels - for all connections with non-blocking I/O multiplexing.</p> - </section> </chapter> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 5098d26a3a..1645eb15f3 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1999</year><year>2012</year> + <year>1999</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -36,18 +36,22 @@ <list type="bulleted"> <item>ssl requires the crypto and public_key applications.</item> - <item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0, experimental - support for TLS-1.1 and TLS-1.2 is also available (no support for elliptic curve cipher suites yet).</item> + <item>Supported SSL/TLS-versions are SSL-3.0, TLS-1.0, + TLS-1.1 and TLS-1.2.</item> <item>For security reasons sslv2 is not supported.</item> <item>Ephemeral Diffie-Hellman cipher suites are supported but not Diffie Hellman Certificates cipher suites.</item> + <item>Elliptic Curve cipher suites are supported if crypto + supports it and named curves are used. + </item> <item>Export cipher suites are not supported as the U.S. lifted its export restrictions in early 2000.</item> <item>IDEA cipher suites are not supported as they have become deprecated by the latest TLS spec so there is not any real motivation to implement them.</item> - <item>CRL and policy certificate - extensions are not supported yet. </item> + <item>CRL and policy certificate extensions are not supported + yet. However CRL verification is supported by public_key, only not integrated + in ssl yet. </item> </list> </section> @@ -75,17 +79,23 @@ {fail_if_no_peer_cert, boolean()} {depth, integer()} | {cert, der_encoded()}| {certfile, path()} | - {key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'PrivateKeyInfo', der_encoded()}} | + {key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey' |'PrivateKeyInfo', der_encoded()}} | {keyfile, path()} | {password, string()} | {cacerts, [der_encoded()]} | {cacertfile, path()} | |{dh, der_encoded()} | {dhfile, path()} | {ciphers, ciphers()} | - {ssl_imp, ssl_imp()}| {reuse_sessions, boolean()} | {reuse_session, fun()} + {user_lookup_fun, {fun(), term()}}, {psk_identity, string()}, {srp_identity, {string(), string()}} | + {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()} + {next_protocols_advertised, [binary()]} | + {client_preferred_next_protocols, client | server, [binary()]} </c></p> - <p><c>transportoption() = {CallbackModule, DataTag, ClosedTag} - - defaults to {gen_tcp, tcp, tcp_closed}. Ssl may be - run over any reliable transport protocol that has - an equivalent API to gen_tcp's.</c></p> + <p><c>transportoption() = {cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom(), ErrTag:atom()}} + - defaults to {gen_tcp, tcp, tcp_closed, tcp_error}. Can be used to customize + the transport layer. The callback module must implement a reliable transport + protocol and behave as gen_tcp and in addition have functions corresponding to + inet:setopts/2, inet:getopts/2, inet:peername/1, inet:sockname/1 and inet:port/1. + The callback gen_tcp is treated specially and will call inet directly. + </c></p> <p><c> CallbackModule = atom()</c> @@ -118,6 +128,8 @@ {key_exchange(), cipher(), hash()}</c></p> <p><c>key_exchange() = rsa | dhe_dss | dhe_rsa | dh_anon + | psk | dhe_psk | rsa_psk | srp_anon | srp_dss | srp_rsa + | ecdh_anon | ecdh_ecdsa | ecdhe_ecdsa | ecdh_rsa | ecdhe_rsa </c></p> <p><c>cipher() = rc4_128 | des_cbc | '3des_ede_cbc' @@ -129,6 +141,9 @@ <p><c>prf_random() = client_random | server_random </c></p> + <p><c>srp_param_type() = srp_1024 | srp_1536 | srp_2048 | srp_3072 + | srp_4096 | srp_6144 | srp_8192</c></p> + </section> <section> @@ -147,7 +162,7 @@ <tag>{certfile, path()}</tag> <item>Path to a file containing the user's certificate.</item> - <tag>{key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'PrivateKeyInfo', der_encoded()}}</tag> + <tag>{key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey' |'PrivateKeyInfo', der_encoded()}}</tag> <item> The DER encoded users private key. If this option is supplied it will override the keyfile option.</item> @@ -174,13 +189,16 @@ <tag>{ciphers, ciphers()}</tag> <item>The cipher suites that should be supported. The function - <c>cipher_suites/0</c> can be used to find all available - ciphers. Additionally some anonymous cipher suites ({dh_anon, - rc4_128, md5}, {dh_anon, des_cbc, sha}, {dh_anon, - '3des_ede_cbc', sha}, {dh_anon, aes_128_cbc, sha}, {dh_anon, - aes_256_cbc, sha}) are supported for testing purposes and will - only work if explicitly enabled by this option and they are supported/enabled - by the peer also. + <c>cipher_suites/0</c> can be used to find all ciphers that are + supported by default. <c>cipher_suites(all)</c> may be called + to find all available cipher suites. + Pre-Shared Key (<url href="http://www.ietf.org/rfc/rfc4279.txt">RFC 4279</url> and + <url href="http://www.ietf.org/rfc/rfc5487.txt">RFC 5487</url>), + Secure Remote Password (<url href="http://www.ietf.org/rfc/rfc5054.txt">RFC 5054</url>) + and anonymous cipher suites only work if explicitly enabled by + this option and they are supported/enabled by the peer also. + Note that anonymous cipher suites are supported for testing purposes + only and should not be used when security matters. </item> <tag>{ssl_imp, new | old}</tag> @@ -190,10 +208,10 @@ <tag>{secure_renegotiate, boolean()}</tag> <item>Specifies if to reject renegotiation attempt that does - not live up to RFC 5746. By default secure_renegotiate is + not live up to <url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url>. By default secure_renegotiate is set to false i.e. secure renegotiation will be used if possible but it will fallback to unsecure renegotiation if the peer - does not support RFC 5746. + does not support <url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url>. </item> <tag>{depth, integer()}</tag> @@ -273,13 +291,46 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <p> {bad_cert, cert_expired}, {bad_cert, invalid_issuer}, {bad_cert, invalid_signature}, {bad_cert, unknown_ca},{bad_cert, selfsigned_peer}, {bad_cert, name_not_permitted}, {bad_cert, missing_basic_constraint}, {bad_cert, invalid_key_usage}</p> </item> + <tag>{versions, [protocol()]}</tag> + <item>TLS protocol versions that will be supported by started clients and servers. + This option overrides the application environment option <c>protocol_version</c>. If the + environment option is not set it defaults to all versions supported by the SSL application. See also + <seealso marker="ssl:ssl_app">ssl(6)</seealso> + </item> + <tag>{hibernate_after, integer()|undefined}</tag> - <item>When an integer-value is specified, the <code>ssl_connection</code> + <item>When an integer-value is specified, the <c>ssl_connection</c> will go into hibernation after the specified number of milliseconds of inactivity, thus reducing its memory footprint. When - <code>undefined</code> is specified (this is the default), the process + <c>undefined</c> is specified (this is the default), the process will never go into hibernation. </item> + + <tag>{user_lookup_fun, {Lookupfun :: fun(), UserState :: term()}}</tag> + <item> + <p>The lookup fun should be defined as:</p> + <code> +fun(psk, PSKIdentity ::string(), UserState :: term()) -> + {ok, SharedSecret :: binary()} | error; +fun(srp, Username :: string(), UserState :: term()) -> + {ok, {SRPParams :: srp_param_type(), Salt :: binary(), DerivedKey :: binary()}} | error. + </code> + + <p>For Pre-Shared Key (PSK) cipher suites, the lookup fun will + be called by the client and server to determine the shared + secret. When called by the client, PSKIdentity will be set to the + hint presented by the server or undefined. When called by the + server, PSKIdentity is the identity presented by the client. + </p> + + <p>For Secure Remote Password (SRP), the fun will only be used by the server to obtain + parameters that it will use to generate its session keys. <c>DerivedKey</c> should be + derived according to <url href="http://tools.ietf.org/html/rfc2945#section-3"> RFC 2945</url> and + <url href="http://tools.ietf.org/html/rfc5054#section-2.4"> RFC 5054</url>: + <c>crypto:sha([Salt, crypto:sha([Username, <<$:>>, Password])]) </c> + </p> + </item> + </taglist> </section> @@ -301,8 +352,37 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | when possible. </item> + <tag>{client_preferred_next_protocols, Precedence :: server | client, ClientPrefs :: [binary()]}</tag> + <tag>{client_preferred_next_protocols, Precedence :: server | client, ClientPrefs :: [binary()], Default :: binary()}</tag> + <item> + <p>Indicates the client will try to perform Next Protocol + Negotiation.</p> + + <p>If precedence is server the negotiated protocol will be the + first protocol that appears on the server advertised list that is + also on the client preference list.</p> + + <p>If precedence is client the negotiated protocol will be the + first protocol that appears on the client preference list that is + also on the server advertised list.</p> + + <p>If the client does not support any of the server advertised + protocols or the server does not advertise any protocols the + client will fallback to the first protocol in its list or if a + default is supplied it will fallback to that instead. If the + server does not support Next Protocol Negotiation the + connection will be aborted if no default protocol is supplied.</p> + </item> + + <tag>{psk_identity, string()}</tag> + <item>Specifies the identity the client presents to the server. The matching secret is + found by calling the user_look_fun. + </item> + <tag>{srp_identity, {Username :: string(), Password :: string()}</tag> + <item>Specifies the Username and Password to use to authenticate to the server. + </item> </taglist> - </section> + </section> <section> <title>SSL OPTION DESCRIPTIONS - SERVER SIDE</title> @@ -353,6 +433,18 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | SuggestedSessionId is a binary(), PeerCert is a DER encoded certificate, Compression is an enumeration integer and CipherSuite is of type ciphersuite(). + </item> + + <tag>{next_protocols_advertised, Protocols :: [binary()]}</tag> + <item>The list of protocols to send to the client if the client indicates + it supports the Next Protocol extension. The client may select a protocol + that is not on this list. The list of protocols must not contain an empty + binary. If the server negotiates a Next Protocol it can be accessed + using <c>negotiated_next_protocol/1</c> method. + </item> + + <tag>{psk_identity, string()}</tag> + <item>Specifies the server identity hint the server presents to the client. </item> </taglist> @@ -386,13 +478,16 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <name>cipher_suites(Type) -> ciphers()</name> <fsummary> Returns a list of supported cipher suites</fsummary> <type> - <v>Type = erlang | openssl</v> + <v>Type = erlang | openssl | all</v> </type> <desc><p>Returns a list of supported cipher suites. cipher_suites() is equivalent to cipher_suites(erlang). Type openssl is provided for backwards compatibility with - old ssl that used openssl. + old ssl that used openssl. cipher_suites(all) returns + all available cipher suites. The cipher suites not present + in cipher_suites(erlang) but in included in cipher_suites(all) + will not be used unless explicitly configured by the user. </p> </desc> </func> @@ -766,8 +861,22 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | ssl application.</p> </desc> </func> + <func> + <name>negotiated_next_protocol(Socket) -> {ok, Protocol} | {error, next_protocol_not_negotiated}</name> + <fsummary>Returns the Next Protocol negotiated.</fsummary> + <type> + <v>Socket = sslsocket()</v> + <v>Protocol = binary()</v> + </type> + <desc> + <p> + Returns the Next Protocol negotiated. + </p> + </desc> + </func> + </funcs> - + <section> <title>SEE ALSO</title> <p><seealso marker="kernel:inet">inet(3) </seealso> and diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml index 2ba6f48611..0ee5b23e47 100644 --- a/lib/ssl/doc/src/ssl_app.xml +++ b/lib/ssl/doc/src/ssl_app.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE appref SYSTEM "appref.dtd"> <appref> <header> <copyright> - <year>1999</year><year>2010</year> + <year>1999</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -29,10 +29,21 @@ sockets.</appsummary> <section> - <title>Environment</title> + <title>DEPENDENCIES</title> + <p>The ssl application uses the Erlang applications public_key and + crypto to handle public keys and encryption, hence these + applications needs to be loaded for the ssl application to work. In + an embedded environment that means they need to be started with + application:start/[1,2] before the ssl application is started. + </p> + </section> + + <section> + <title>ENVIRONMENT</title> <p>The following application environment configuration parameters - are defined for the SSL application. Refer to application(3) for - more information about configuration parameters. + are defined for the SSL application. See <seealso + marker="kernel:application">application(3)</seealso>for more + information about configuration parameters. </p> <p>Note that the environment parameters can be set on the command line, for instance,</p> @@ -77,7 +88,7 @@ <section> <title>SEE ALSO</title> - <p>application(3)</p> + <p><seealso marker="kernel:application">application(3)</seealso></p> </section> </appref> diff --git a/lib/ssl/doc/src/ssl_protocol.xml b/lib/ssl/doc/src/ssl_protocol.xml index 17268a634d..f540dc999b 100644 --- a/lib/ssl/doc/src/ssl_protocol.xml +++ b/lib/ssl/doc/src/ssl_protocol.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2011</year> + <year>2003</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -25,9 +25,8 @@ <file>ssl_protocol.xml</file> </header> - <p>The erlang SSL application currently supports SSL 3.0 and TLS 1.0 - RFC 2246, and will in the future also support later versions of TLS. - SSL 2.0 is not supported. + <p>The erlang SSL application currently implements the protocol SSL/TLS + for currently supported versions see <seealso marker="ssl">ssl(3)</seealso> </p> <p>By default erlang SSL is run over the TCP/IP protocol even diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index c5c5bf593a..cf9f7d5001 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2012. All Rights Reserved. +# Copyright Ericsson AB 1999-2013. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -42,31 +42,37 @@ BEHAVIOUR_MODULES= \ MODULES= \ ssl \ + tls \ + dtls \ ssl_alert \ ssl_app \ ssl_dist_sup\ ssl_sup \ inet_tls_dist \ ssl_certificate\ - ssl_certificate_db\ + ssl_pkix_db\ ssl_cipher \ - ssl_connection \ + ssl_srp_primes \ + tls_connection \ + dtls_connection \ ssl_connection_sup \ - ssl_debug \ - ssl_handshake \ + tls_handshake \ + dtls_handshake\ ssl_manager \ ssl_session \ ssl_session_cache \ - ssl_record \ + ssl_socket \ + tls_record \ + dtls_record \ ssl_ssl2 \ ssl_ssl3 \ ssl_tls1 \ ssl_tls_dist_proxy INTERNAL_HRL_FILES = \ - ssl_debug.hrl \ - ssl_alert.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_internal.hrl \ - ssl_record.hrl + ssl_alert.hrl ssl_cipher.hrl ssl_handshake.hrl tls_handshake.hrl \ + dtls_handshake.hrl ssl_internal.hrl \ + ssl_record.hrl tls_record.hrl dtls_record.hrl ssl_srp.hrl ERL_FILES= \ $(MODULES:%=%.erl) \ @@ -108,10 +114,10 @@ clean: rm -f errs core *~ $(APP_TARGET): $(APP_SRC) ../vsn.mk - sed -e 's;%VSN%;$(VSN);' $< > $@ + $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk - sed -e 's;%VSN%;$(VSN);' $< > $@ + $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ docs: @@ -130,3 +136,24 @@ release_spec: opt release_docs_spec: +# ---------------------------------------------------- +# Dependencies +# ---------------------------------------------------- +$(EBIN)/inet_tls_dist.$(EMULATOR): ../../kernel/include/net_address.hrl ../../kernel/include/dist.hrl ../../kernel/include/dist_util.hrl +$(EBIN)/tls.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ../../public_key/include/public_key.hrl +$(EBIN)/ssl_alert.$(EMULATOR): ssl_alert.hrl ssl_record.hrl +$(EBIN)/ssl_certificate.$(EMULATOR): ssl_internal.hrl ssl_alert.hrl ssl_handshake.hrl ../../public_key/include/public_key.hrl +$(EBIN)/ssl_certificate_db.$(EMULATOR): ssl_internal.hrl ../../public_key/include/public_key.hrl ../../kernel/include/file.hrl +$(EBIN)/ssl_cipher.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl +$(EBIN)/tls_connection.$(EMULATOR): ssl_internal.hrl tls_record.hrl ssl_cipher.hrl tls_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl +$(EBIN)/dtls_connection.$(EMULATOR): ssl_internal.hrl dtls_record.hrl ssl_cipher.hrl dtls_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl +$(EBIN)/tls_handshake.$(EMULATOR): ssl_internal.hrl tls_record.hrl ssl_cipher.hrl tls_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl +$(EBIN)/ssl_manager.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl ../../kernel/include/file.hrl +$(EBIN)/ssl_record.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl +$(EBIN)/ssl_session.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl +$(EBIN)/ssl_session_cache.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl +$(EBIN)/ssl_session_cache_api.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl +$(EBIN)/ssl_ssl3.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl +$(EBIN)/ssl_tls1.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl + + diff --git a/lib/ssl/src/ssl_debug.hrl b/lib/ssl/src/dtls.erl index e88cef441f..013286c9bd 100644 --- a/lib/ssl/src/ssl_debug.hrl +++ b/lib/ssl/src/dtls.erl @@ -1,39 +1,25 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% %% +%%% Purpose : API for DTLS. --ifndef(ssl_debug). --define(ssl_debug, true). - --ifdef(SSL_DEBUG). --define(DBG_HEX(V), ssl_debug:hex_data(??V, V, ?MODULE, ?LINE)). --define(DBG_TERM(T), ssl_debug:term_data(??T, T, ?MODULE, ?LINE)). --else. --define(DBG_HEX(V), ok). --define(DBG_TERM(T), ok). --endif. - --endif. % -ifdef(ssl_debug). - - - - +-module(dtls). diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl new file mode 100644 index 0000000000..ac2ee0d09f --- /dev/null +++ b/lib/ssl/src/dtls_connection.erl @@ -0,0 +1,19 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013-2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(dtls_connection). diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl new file mode 100644 index 0000000000..b25daa59d9 --- /dev/null +++ b/lib/ssl/src/dtls_handshake.erl @@ -0,0 +1,18 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013-2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +-module(dtls_handshake). diff --git a/lib/ssl/src/dtls_handshake.hrl b/lib/ssl/src/dtls_handshake.hrl new file mode 100644 index 0000000000..db7b8596ae --- /dev/null +++ b/lib/ssl/src/dtls_handshake.hrl @@ -0,0 +1,50 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013-2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Record and constant defenitions for the DTLS-handshake protocol +%% that differs from TLS see RFC 6347 +%%---------------------------------------------------------------------- +-ifndef(dtls_handshake). +-define(dtls_handshake, true). + +-include("ssl_handshake.hrl"). %% Common TLS and DTLS records and Constantes + +-record(client_hello, { + client_version, + random, + session_id, % opaque SessionID<0..32> + cookie, % opaque<2..2^16-1> + cipher_suites, % cipher_suites<2..2^16-1> + compression_methods, % compression_methods<1..2^8-1>, + %% Extensions + renegotiation_info, + hash_signs, % supported combinations of hashes/signature algos + next_protocol_negotiation = undefined % [binary()] + }). + +-record(hello_verify_request { + protocol_version, + cookie + }). + +-define(HELLO_VERIFY_REQUEST, 3). + +-endif. % -ifdef(dtls_handshake). diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl new file mode 100644 index 0000000000..2469a7d26c --- /dev/null +++ b/lib/ssl/src/dtls_record.erl @@ -0,0 +1,18 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013-2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +-module(dtls_record). diff --git a/lib/ssl/src/dtls_record.hrl b/lib/ssl/src/dtls_record.hrl new file mode 100644 index 0000000000..e935d84bdf --- /dev/null +++ b/lib/ssl/src/dtls_record.hrl @@ -0,0 +1,44 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013-2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Record and constant defenitions for the DTLS-record protocol +%% see RFC 6347 +%%---------------------------------------------------------------------- + +-ifndef(dtls_record). +-define(dtls_record, true). + +-include("ssl_record.hrl"). %% Common TLS and DTLS records and Constantes + +%% Used to handle tls_plain_text, tls_compressed and tls_cipher_text + +-record(ssl_tls, { + type, + version, + record_seq, % used in plain_text + epoch, % used in plain_text + message_seq, + fragment_offset, + fragment_length, + fragment + }). + +-endif. % -ifdef(dtls_record). diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index 13d5eaf4d7..582a60635f 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -1,7 +1,20 @@ {application, ssl, [{description, "Erlang/OTP SSL application"}, {vsn, "%VSN%"}, - {modules, [ssl, + {modules, [ + %% TLS/SSL + tls, + tls_connection, + tls_handshake, + tls_record, + %% DTLS + dtls_record, + dtls_handshake, + dtls_connection, + dtls, + %% Backwards compatibility + ssl, + %% Both TLS/SSL and DTLS ssl_app, ssl_sup, inet_tls_dist, @@ -13,14 +26,15 @@ ssl_session, ssl_session_cache_api, ssl_session_cache, - ssl_record, + ssl_socket, + %%ssl_record, ssl_manager, - ssl_handshake, - ssl_debug, + %%ssl_handshake, ssl_connection_sup, - ssl_connection, + %%ssl_connection, ssl_cipher, - ssl_certificate_db, + ssl_srp_primes, + ssl_pkix_db, ssl_certificate, ssl_alert ]}, @@ -30,5 +44,3 @@ {mod, {ssl_app, []}}]}. - - diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index 664f588003..9e5bec26f1 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,34 +1,15 @@ %% -*- erlang -*- {"%VSN%", [ - {"5.1.2", - [ - {restart_application, inets} - ] - }, - {"5.1.1", [{restart_application, ssl}] - }, - {"5.1", [ - {load_module, ssl_connection, soft_purge, soft_purge, []} - ] - }, + {<<"5.2\\*">>, [{restart_application, ssl}]}, + {<<"5.1\\*">>, [{restart_application, ssl}]}, {<<"5.0\\*">>, [{restart_application, ssl}]}, {<<"4\\.*">>, [{restart_application, ssl}]}, {<<"3\\.*">>, [{restart_application, ssl}]} ], [ - {"5.1.2", - [ - {restart_application, inets} - ] - }, - {"5.1.1", [{restart_application, ssl}] - }, - {"5.1", [ - {load_module, ssl_connection, soft_purge, soft_purge, []} - ] - }, - {"5.1", [{restart_application, ssl}]}, + {<<"5.2\\*">>, [{restart_application, ssl}]}, + {<<"5.1\\*">>, [{restart_application, ssl}]}, {<<"5.0\\*">>, [{restart_application, ssl}]}, {<<"4\\.*">>, [{restart_application, ssl}]}, {<<"3\\.*">>, [{restart_application, ssl}]} diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index b52470b988..dc6898d001 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% Copyright Ericsson AB 1999-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -19,7 +19,7 @@ %% -%%% Purpose : Main API module for SSL. +%%% Purpose : Backwards compatibility -module(ssl). @@ -28,16 +28,15 @@ cipher_suites/0, cipher_suites/1, suite_definition/1, close/1, shutdown/2, connect/3, connect/2, connect/4, connection_info/1, - controlling_process/2, listen/2, pid/1, peername/1, peercert/1, + controlling_process/2, listen/2, peername/1, peercert/1, recv/2, recv/3, send/2, getopts/2, setopts/2, sockname/1, versions/0, session_info/1, format_error/1, - renegotiate/1, prf/5, clear_pem_cache/0, random_bytes/1]). - --deprecated({pid, 1, next_major_release}). + renegotiate/1, prf/5, clear_pem_cache/0, random_bytes/1, negotiated_next_protocol/1]). -include("ssl_internal.hrl"). -include("ssl_record.hrl"). -include("ssl_cipher.hrl"). +-include("ssl_handshake.hrl"). -include_lib("public_key/include/public_key.hrl"). @@ -47,13 +46,6 @@ tls_atom_version/0, %% From ssl_internal.hrl prf_random/0, sslsocket/0]). --record(config, {ssl, %% SSL parameters - inet_user, %% User set inet options - emulated, %% #socket_option{} emulated - inet_ssl, %% inet options for internal ssl socket - cb %% Callback info - }). - -type sslsocket() :: #sslsocket{}. -type connect_option() :: socket_connect_option() | ssl_option() | transport_option(). -type socket_connect_option() :: gen_tcp:connect_option(). @@ -66,8 +58,13 @@ {cert, Der::binary()} | {certfile, path()} | {key, Der::binary()} | {keyfile, path()} | {password, string()} | {cacerts, [Der::binary()]} | {cacertfile, path()} | {dh, Der::binary()} | {dhfile, path()} | + {user_lookup_fun, {fun(), InitialUserState::term()}} | + {psk_identity, string()} | + {srp_identity, {string(), string()}} | {ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | - {reuse_session, fun()} | {hibernate_after, integer()|undefined}. + {reuse_session, fun()} | {hibernate_after, integer()|undefined} | + {next_protocols_advertised, list(binary())} | + {client_preferred_next_protocols, binary(), client | server, list(binary())}. -type verify_type() :: verify_none | verify_peer. -type path() :: string(). @@ -75,7 +72,8 @@ string(). % (according to old API) -type ssl_imp() :: new | old. --type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom()}}. +-type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(), + ClosedTag::atom(), ErrTag::atom()}}. -type prf_random() :: client_random | server_random. %%-------------------------------------------------------------------- @@ -87,772 +85,137 @@ %% is temporary. see application(3) %%-------------------------------------------------------------------- start() -> - application:start(crypto), - application:start(public_key), - application:start(ssl). - + tls:start(). start(Type) -> - application:start(crypto, Type), - application:start(public_key, Type), - application:start(ssl, Type). + tls:start(Type). -%%-------------------------------------------------------------------- --spec stop() -> ok. -%% -%% Description: Stops the ssl application. -%%-------------------------------------------------------------------- stop() -> - application:stop(ssl). + tls:stop(). -%%-------------------------------------------------------------------- --spec connect(host() | port(), [connect_option()]) -> {ok, #sslsocket{}} | - {error, reason()}. --spec connect(host() | port(), [connect_option()] | inet:port_number(), timeout() | list()) -> - {ok, #sslsocket{}} | {error, reason()}. --spec connect(host() | port(), inet:port_number(), list(), timeout()) -> - {ok, #sslsocket{}} | {error, reason()}. - -%% -%% Description: Connect to an ssl server. -%%-------------------------------------------------------------------- -connect(Socket, SslOptions) when is_port(Socket) -> - connect(Socket, SslOptions, infinity). - -connect(Socket, SslOptions0, Timeout) when is_port(Socket) -> - EmulatedOptions = emulated_options(), - {ok, InetValues} = inet:getopts(Socket, EmulatedOptions), - ok = inet:setopts(Socket, internal_inet_values()), - try handle_options(SslOptions0 ++ InetValues, client) of - {ok, #config{cb=CbInfo, ssl=SslOptions, emulated=EmOpts}} -> - case inet:peername(Socket) of - {ok, {Address, Port}} -> - ssl_connection:connect(Address, Port, Socket, - {SslOptions, EmOpts}, - self(), CbInfo, Timeout); - {error, Error} -> - {error, Error} - end - catch - _:{error, Reason} -> - {error, Reason} - end; +connect(Socket, SslOptions) -> + tls:connect(Socket, SslOptions). -connect(Host, Port, Options) -> - connect(Host, Port, Options, infinity). +connect(Socket, SslOptions0, TimeoutOrOpts) -> + tls:connect(Socket, SslOptions0, TimeoutOrOpts). connect(Host, Port, Options, Timeout) -> - try handle_options(Options, client) of - {ok, Config} -> - do_connect(Host,Port,Config,Timeout) - catch - throw:Error -> - Error - end. + tls:connect(Host, Port, Options, Timeout). -%%-------------------------------------------------------------------- --spec listen(inet:port_number(), [listen_option()]) ->{ok, #sslsocket{}} | {error, reason()}. - -%% -%% Description: Creates an ssl listen socket. -%%-------------------------------------------------------------------- -listen(_Port, []) -> - {error, enooptions}; -listen(Port, Options0) -> - try - {ok, Config} = handle_options(Options0, server), - #config{cb={CbModule, _, _, _},inet_user=Options} = Config, - case CbModule:listen(Port, Options) of - {ok, ListenSocket} -> - {ok, #sslsocket{pid = {ListenSocket, Config}, fd = new_ssl}}; - Err = {error, _} -> - Err - end - catch - Error = {error, _} -> - Error - end. -%%-------------------------------------------------------------------- --spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}} | - {error, reason()}. --spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | - {error, reason()}. -%% -%% Description: Performs transport accept on an ssl listen socket -%%-------------------------------------------------------------------- -transport_accept(ListenSocket) -> - transport_accept(ListenSocket, infinity). +listen(Port, Options) -> + tls:listen(Port, Options). -transport_accept(#sslsocket{pid = {ListenSocket, #config{cb=CbInfo, ssl=SslOpts}}}, Timeout) -> - - %% The setopt could have been invoked on the listen socket - %% and options should be inherited. - EmOptions = emulated_options(), - {ok, InetValues} = inet:getopts(ListenSocket, EmOptions), - ok = inet:setopts(ListenSocket, internal_inet_values()), - {CbModule,_,_, _} = CbInfo, - case CbModule:accept(ListenSocket, Timeout) of - {ok, Socket} -> - ok = inet:setopts(ListenSocket, InetValues), - {ok, Port} = inet:port(Socket), - ConnArgs = [server, "localhost", Port, Socket, - {SslOpts, socket_options(InetValues)}, self(), CbInfo], - case ssl_connection_sup:start_child(ConnArgs) of - {ok, Pid} -> - ssl_connection:socket_control(Socket, Pid, CbModule); - {error, Reason} -> - {error, Reason} - end; - {error, Reason} -> - {error, Reason} - end. +transport_accept(ListenSocket) -> + tls:transport_accept(ListenSocket). -%%-------------------------------------------------------------------- --spec ssl_accept(#sslsocket{}) -> ok | {error, reason()}. --spec ssl_accept(#sslsocket{} | port(), timeout()| [ssl_option() | transport_option()]) -> - ok | {ok, #sslsocket{}} | {error, reason()}. --spec ssl_accept(port(), [ssl_option()| transport_option()], timeout()) -> {ok, #sslsocket{}} | {error, reason()}. -%% -%% Description: Performs accept on an ssl listen socket. e.i. performs -%% ssl handshake. -%%-------------------------------------------------------------------- +transport_accept(ListenSocket, Timeout) -> + tls:transport_accept(ListenSocket, Timeout). + ssl_accept(ListenSocket) -> - ssl_accept(ListenSocket, infinity). + tls:ssl_accept(ListenSocket, infinity). ssl_accept(#sslsocket{} = Socket, Timeout) -> - ssl_connection:handshake(Socket, Timeout); + tls:ssl_accept(Socket, Timeout); ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) -> - ssl_accept(ListenSocket, SslOptions, infinity). + tls:ssl_accept(ListenSocket, SslOptions, infinity). ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> - EmulatedOptions = emulated_options(), - {ok, InetValues} = inet:getopts(Socket, EmulatedOptions), - ok = inet:setopts(Socket, internal_inet_values()), - try handle_options(SslOptions ++ InetValues, server) of - {ok, #config{cb=CbInfo,ssl=SslOpts, emulated=EmOpts}} -> - {ok, Port} = inet:port(Socket), - ssl_connection:ssl_accept(Port, Socket, - {SslOpts, EmOpts}, - self(), CbInfo, Timeout) - catch - Error = {error, _Reason} -> Error - end. + tls:ssl_accept(Socket, SslOptions, Timeout). -%%-------------------------------------------------------------------- --spec close(#sslsocket{}) -> term(). -%% -%% Description: Close an ssl connection -%%-------------------------------------------------------------------- -close(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}}) -> - CbMod:close(ListenSocket); -close(#sslsocket{pid = Pid}) -> - ssl_connection:close(Pid). +close(Socket) -> + tls:close(Socket). -%%-------------------------------------------------------------------- --spec send(#sslsocket{}, iodata()) -> ok | {error, reason()}. -%% -%% Description: Sends data over the ssl connection -%%-------------------------------------------------------------------- -send(#sslsocket{pid = Pid}, Data) -> - ssl_connection:send(Pid, Data). +send(Socket, Data) -> + tls:send(Socket, Data). -%%-------------------------------------------------------------------- --spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}. --spec recv(#sslsocket{}, integer(), timeout()) -> {ok, binary()| list()} | {error, reason()}. -%% -%% Description: Receives data when active = false -%%-------------------------------------------------------------------- recv(Socket, Length) -> - recv(Socket, Length, infinity). -recv(#sslsocket{pid = Pid, fd = new_ssl}, Length, Timeout) -> - ssl_connection:recv(Pid, Length, Timeout). + tls:recv(Socket, Length, infinity). +recv(Socket, Length, Timeout) -> + tls:recv(Socket, Length, Timeout). -%%-------------------------------------------------------------------- --spec controlling_process(#sslsocket{}, pid()) -> ok | {error, reason()}. -%% -%% Description: Changes process that receives the messages when active = true -%% or once. -%%-------------------------------------------------------------------- -controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid) -> - ssl_connection:new_user(Pid, NewOwner). - -%%-------------------------------------------------------------------- --spec connection_info(#sslsocket{}) -> {ok, {tls_atom_version(), erl_cipher_suite()}} | - {error, reason()}. -%% -%% Description: Returns ssl protocol and cipher used for the connection -%%-------------------------------------------------------------------- -connection_info(#sslsocket{pid = Pid}) -> - ssl_connection:info(Pid). +controlling_process(Socket, NewOwner) -> + tls:controlling_process(Socket, NewOwner). + +connection_info(Socket) -> + tls:connection_info(Socket). -%%-------------------------------------------------------------------- --spec peername(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}. -%% -%% Description: same as inet:peername/1. -%%-------------------------------------------------------------------- -peername(#sslsocket{pid = Pid}) -> - ssl_connection:peername(Pid). +peername(Socket) -> + tls:peername(Socket). -%%-------------------------------------------------------------------- --spec peercert(#sslsocket{}) ->{ok, DerCert::binary()} | {error, reason()}. -%% -%% Description: Returns the peercert. -%%-------------------------------------------------------------------- -peercert(#sslsocket{pid = Pid}) -> - case ssl_connection:peer_certificate(Pid) of +peercert(#sslsocket{pid = Pid}) when is_pid(Pid) -> + case tls_connection:peer_certificate(Pid) of {ok, undefined} -> {error, no_peercert}; Result -> Result - end. + end; +peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> + {error, enotconn}. -%%-------------------------------------------------------------------- --spec suite_definition(cipher_suite()) -> erl_cipher_suite(). -%% -%% Description: Return erlang cipher suite definition. -%%-------------------------------------------------------------------- suite_definition(S) -> {KeyExchange, Cipher, Hash, _} = ssl_cipher:suite_definition(S), {KeyExchange, Cipher, Hash}. -%%-------------------------------------------------------------------- +negotiated_next_protocol(#sslsocket{pid = Pid}) -> + tls_connection:negotiated_next_protocol(Pid). + +%%%-------------------------------------------------------------------- -spec cipher_suites() -> [erl_cipher_suite()]. --spec cipher_suites(erlang | openssl) -> [erl_cipher_suite()] | [string()]. +-spec cipher_suites(erlang | openssl | all ) -> [erl_cipher_suite()] | [string()]. %% Description: Returns all supported cipher suites. %%-------------------------------------------------------------------- + cipher_suites() -> cipher_suites(erlang). cipher_suites(erlang) -> - Version = ssl_record:highest_protocol_version([]), + Version = tls_record:highest_protocol_version([]), [suite_definition(S) || S <- ssl_cipher:suites(Version)]; cipher_suites(openssl) -> - Version = ssl_record:highest_protocol_version([]), - [ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)]. + Version = tls_record:highest_protocol_version([]), + [ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)]; -%%-------------------------------------------------------------------- --spec getopts(#sslsocket{}, [gen_tcp:option_name()]) -> - {ok, [gen_tcp:option()]} | {error, reason()}. -%% -%% Description: Gets options -%%-------------------------------------------------------------------- -getopts(#sslsocket{pid = Pid}, OptionTags) when is_pid(Pid), is_list(OptionTags) -> - ssl_connection:get_opts(Pid, OptionTags); -getopts(#sslsocket{pid = {ListenSocket, _}}, OptionTags) when is_list(OptionTags) -> - try inet:getopts(ListenSocket, OptionTags) of - {ok, _} = Result -> - Result; - {error, InetError} -> - {error, {eoptions, {inet_options, OptionTags, InetError}}} - catch - _:_ -> - {error, {eoptions, {inet_options, OptionTags}}} - end; -getopts(#sslsocket{}, OptionTags) -> - {error, {eoptions, {inet_options, OptionTags}}}. - -%%-------------------------------------------------------------------- --spec setopts(#sslsocket{}, [gen_tcp:option()]) -> ok | {error, reason()}. -%% -%% Description: Sets options -%%-------------------------------------------------------------------- -setopts(#sslsocket{pid = Pid}, Options0) when is_pid(Pid), is_list(Options0) -> - try proplists:expand([{binary, [{mode, binary}]}, - {list, [{mode, list}]}], Options0) of - Options -> - ssl_connection:set_opts(Pid, Options) - catch - _:_ -> - {error, {eoptions, {not_a_proplist, Options0}}} - end; +cipher_suites(all) -> + Version = tls_record:highest_protocol_version([]), + Supported = ssl_cipher:suites(Version) + ++ ssl_cipher:anonymous_suites() + ++ ssl_cipher:psk_suites(Version) + ++ ssl_cipher:srp_suites(), + [suite_definition(S) || S <- Supported]. -setopts(#sslsocket{pid = {ListenSocket, _}}, Options) when is_list(Options) -> - try inet:setopts(ListenSocket, Options) of - ok -> - ok; - {error, InetError} -> - {error, {eoptions, {inet_options, Options, InetError}}} - catch - _:Error -> - {error, {eoptions, {inet_options, Options, Error}}} - end; -setopts(#sslsocket{}, Options) -> - {error, {eoptions,{not_a_proplist, Options}}}. +getopts(Socket, OptionTags) -> + tls:getopts(Socket, OptionTags). -%%--------------------------------------------------------------- --spec shutdown(#sslsocket{}, read | write | read_write) -> ok | {error, reason()}. -%% -%% Description: Same as gen_tcp:shutdown/2 -%%-------------------------------------------------------------------- -shutdown(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}}, How) -> - CbMod:shutdown(ListenSocket, How); -shutdown(#sslsocket{pid = Pid}, How) -> - ssl_connection:shutdown(Pid, How). +setopts(Socket, Options) -> + tls:setopts(Socket, Options). -%%-------------------------------------------------------------------- --spec sockname(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}. -%% -%% Description: Same as inet:sockname/1 -%%-------------------------------------------------------------------- -sockname(#sslsocket{pid = {ListenSocket, _}}) -> - inet:sockname(ListenSocket); +shutdown(Socket, How) -> + tls:shutdown(Socket, How). -sockname(#sslsocket{pid = Pid}) -> - ssl_connection:sockname(Pid). +sockname(Socket) -> + tls:sockname(Socket). -%%--------------------------------------------------------------- --spec session_info(#sslsocket{}) -> {ok, list()} | {error, reason()}. -%% -%% Description: Returns list of session info currently [{session_id, session_id(), -%% {cipher_suite, cipher_suite()}] -%%-------------------------------------------------------------------- -session_info(#sslsocket{pid = Pid, fd = new_ssl}) -> - ssl_connection:session_info(Pid). +session_info(#sslsocket{pid = Pid}) when is_pid(Pid) -> + tls_connection:session_info(Pid); +session_info(#sslsocket{pid = {Listen,_}}) when is_port(Listen) -> + {error, enotconn}. -%%--------------------------------------------------------------- --spec versions() -> [{ssl_app, string()} | {supported, [tls_atom_version()]} | - {available, [tls_atom_version()]}]. -%% -%% Description: Returns a list of relevant versions. -%%-------------------------------------------------------------------- versions() -> - Vsns = ssl_record:supported_protocol_versions(), - SupportedVsns = [ssl_record:protocol_version(Vsn) || Vsn <- Vsns], - AvailableVsns = ?ALL_SUPPORTED_VERSIONS, - [{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}]. - - -%%--------------------------------------------------------------- --spec renegotiate(#sslsocket{}) -> ok | {error, reason()}. -%% -%% Description: Initiates a renegotiation. -%%-------------------------------------------------------------------- -renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) -> - ssl_connection:renegotiation(Pid). + tls:versions(). -%%-------------------------------------------------------------------- --spec prf(#sslsocket{}, binary() | 'master_secret', binary(), - binary() | prf_random(), non_neg_integer()) -> - {ok, binary()} | {error, reason()}. -%% -%% Description: use a ssl sessions TLS PRF to generate key material -%%-------------------------------------------------------------------- -prf(#sslsocket{pid = Pid, fd = new_ssl}, - Secret, Label, Seed, WantedLength) -> - ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength). +renegotiate(Socket) -> + tls:renegotiate(Socket). +prf(Socket, Secret, Label, Seed, WantedLength) -> + tls:prf(Socket, Secret, Label, Seed, WantedLength). -%%-------------------------------------------------------------------- --spec clear_pem_cache() -> ok. -%% -%% Description: Clear the PEM cache -%%-------------------------------------------------------------------- clear_pem_cache() -> - ssl_manager:clear_pem_cache(). + tls:clear_pem_cache(). -%%--------------------------------------------------------------- --spec format_error({error, term()}) -> list(). -%% -%% Description: Creates error string. -%%-------------------------------------------------------------------- -format_error({error, Reason}) -> - format_error(Reason); -format_error(Reason) when is_list(Reason) -> - Reason; -format_error(closed) -> - "The connection is closed"; -format_error(ecacertfile) -> - "Own CA certificate file is invalid."; -format_error(ecertfile) -> - "Own certificate file is invalid."; -format_error(ekeyfile) -> - "Own private key file is invalid."; -format_error(esslaccept) -> - "Server SSL handshake procedure between client and server failed."; -format_error(esslconnect) -> - "Client SSL handshake procedure between client and server failed."; -format_error({eoptions, Options}) -> - lists:flatten(io_lib:format("Error in options list: ~p~n", [Options])); - -format_error(Error) -> - case (catch inet:format_error(Error)) of - "unkknown POSIX" ++ _ -> - no_format(Error); - {'EXIT', _} -> - no_format(Error); - Other -> - Other - end. - -%%-------------------------------------------------------------------- --spec random_bytes(integer()) -> binary(). +format_error(Error) -> + tls:format_error(Error). -%% -%% Description: Generates cryptographically secure random sequence if possible -%% fallbacks on pseudo random function -%%-------------------------------------------------------------------- random_bytes(N) -> - try crypto:strong_rand_bytes(N) of - RandBytes -> - RandBytes - catch - error:low_entropy -> - crypto:rand_bytes(N) - end. - - -%%%-------------------------------------------------------------- -%%% Internal functions -%%%-------------------------------------------------------------------- -do_connect(Address, Port, - #config{cb=CbInfo, inet_user=UserOpts, ssl=SslOpts, - emulated=EmOpts,inet_ssl=SocketOpts}, - Timeout) -> - {CbModule, _, _, _} = CbInfo, - try CbModule:connect(Address, Port, SocketOpts, Timeout) of - {ok, Socket} -> - ssl_connection:connect(Address, Port, Socket, {SslOpts,EmOpts}, - self(), CbInfo, Timeout); - {error, Reason} -> - {error, Reason} - catch - exit:{function_clause, _} -> - {error, {eoptions, {cb_info, CbInfo}}}; - exit:badarg -> - {error, {eoptions, {inet_options, UserOpts}}}; - exit:{badarg, _} -> - {error, {eoptions, {inet_options, UserOpts}}} - end. - -handle_options(Opts0, _Role) -> - Opts = proplists:expand([{binary, [{mode, binary}]}, - {list, [{mode, list}]}], Opts0), - ReuseSessionFun = fun(_, _, _, _) -> true end, - - DefaultVerifyNoneFun = - {fun(_,{bad_cert, _}, UserState) -> - {valid, UserState}; - (_,{extension, _}, UserState) -> - {unknown, UserState}; - (_, valid, UserState) -> - {valid, UserState}; - (_, valid_peer, UserState) -> - {valid, UserState} - end, []}, - - VerifyNoneFun = handle_option(verify_fun, Opts, DefaultVerifyNoneFun), - - UserFailIfNoPeerCert = handle_option(fail_if_no_peer_cert, Opts, false), - UserVerifyFun = handle_option(verify_fun, Opts, undefined), - CaCerts = handle_option(cacerts, Opts, undefined), - - {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun} = - %% Handle 0, 1, 2 for backwards compatibility - case proplists:get_value(verify, Opts, verify_none) of - 0 -> - {verify_none, false, - ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun}; - 1 -> - {verify_peer, false, - ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun}; - 2 -> - {verify_peer, true, - ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun}; - verify_none -> - {verify_none, false, - ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun}; - verify_peer -> - {verify_peer, UserFailIfNoPeerCert, - ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun}; - Value -> - throw({error, {eoptions, {verify, Value}}}) - end, - - CertFile = handle_option(certfile, Opts, <<>>), - - SSLOptions = #ssl_options{ - versions = handle_option(versions, Opts, []), - verify = validate_option(verify, Verify), - verify_fun = VerifyFun, - fail_if_no_peer_cert = FailIfNoPeerCert, - verify_client_once = handle_option(verify_client_once, Opts, false), - depth = handle_option(depth, Opts, 1), - cert = handle_option(cert, Opts, undefined), - certfile = CertFile, - key = handle_option(key, Opts, undefined), - keyfile = handle_option(keyfile, Opts, CertFile), - password = handle_option(password, Opts, ""), - cacerts = CaCerts, - cacertfile = handle_option(cacertfile, Opts, CaCertDefault), - dh = handle_option(dh, Opts, undefined), - dhfile = handle_option(dhfile, Opts, undefined), - ciphers = handle_option(ciphers, Opts, []), - %% Server side option - reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun), - reuse_sessions = handle_option(reuse_sessions, Opts, true), - secure_renegotiate = handle_option(secure_renegotiate, Opts, false), - renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT), - debug = handle_option(debug, Opts, []), - hibernate_after = handle_option(hibernate_after, Opts, undefined), - erl_dist = handle_option(erl_dist, Opts, false), - log_alert = handle_option(log_alert, Opts, true) - }, - - CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}), - SslOptions = [versions, verify, verify_fun, - fail_if_no_peer_cert, verify_client_once, - depth, cert, certfile, key, keyfile, - password, cacerts, cacertfile, dh, dhfile, ciphers, - debug, reuse_session, reuse_sessions, ssl_imp, - cb_info, renegotiate_at, secure_renegotiate, hibernate_after, erl_dist, log_alert], - - SockOpts = lists:foldl(fun(Key, PropList) -> - proplists:delete(Key, PropList) - end, Opts, SslOptions), - - {SSLsock, Emulated} = emulated_options(SockOpts), - {ok, #config{ssl=SSLOptions, emulated=Emulated, inet_ssl=SSLsock, - inet_user=SockOpts, cb=CbInfo}}. - -handle_option(OptionName, Opts, Default) -> - validate_option(OptionName, - proplists:get_value(OptionName, Opts, Default)). - - -validate_option(versions, Versions) -> - validate_versions(Versions, Versions); -validate_option(verify, Value) - when Value == verify_none; Value == verify_peer -> - Value; -validate_option(verify_fun, undefined) -> - undefined; -%% Backwards compatibility -validate_option(verify_fun, Fun) when is_function(Fun) -> - {fun(_,{bad_cert, _} = Reason, OldFun) -> - case OldFun([Reason]) of - true -> - {valid, OldFun}; - false -> - {fail, Reason} - end; - (_,{extension, _}, UserState) -> - {unknown, UserState}; - (_, valid, UserState) -> - {valid, UserState}; - (_, valid_peer, UserState) -> - {valid, UserState} - end, Fun}; -validate_option(verify_fun, {Fun, _} = Value) when is_function(Fun) -> - Value; -validate_option(fail_if_no_peer_cert, Value) - when Value == true; Value == false -> - Value; -validate_option(verify_client_once, Value) - when Value == true; Value == false -> - Value; -validate_option(depth, Value) when is_integer(Value), - Value >= 0, Value =< 255-> - Value; -validate_option(cert, Value) when Value == undefined; - is_binary(Value) -> - Value; -validate_option(certfile, undefined = Value) -> - Value; -validate_option(certfile, Value) when is_binary(Value) -> - Value; -validate_option(certfile, Value) when is_list(Value) -> - list_to_binary(Value); - -validate_option(key, undefined) -> - undefined; -validate_option(key, {KeyType, Value}) when is_binary(Value), - KeyType == rsa; %% Backwards compatibility - KeyType == dsa; %% Backwards compatibility - KeyType == 'RSAPrivateKey'; - KeyType == 'DSAPrivateKey'; - KeyType == 'PrivateKeyInfo' -> - {KeyType, Value}; - -validate_option(keyfile, undefined) -> - <<>>; -validate_option(keyfile, Value) when is_binary(Value) -> - Value; -validate_option(keyfile, Value) when is_list(Value), Value =/= "" -> - list_to_binary(Value); -validate_option(password, Value) when is_list(Value) -> - Value; - -validate_option(cacerts, Value) when Value == undefined; - is_list(Value) -> - Value; -%% certfile must be present in some cases otherwhise it can be set -%% to the empty string. -validate_option(cacertfile, undefined) -> - <<>>; -validate_option(cacertfile, Value) when is_binary(Value) -> - Value; -validate_option(cacertfile, Value) when is_list(Value), Value =/= ""-> - list_to_binary(Value); -validate_option(dh, Value) when Value == undefined; - is_binary(Value) -> - Value; -validate_option(dhfile, undefined = Value) -> - Value; -validate_option(dhfile, Value) when is_binary(Value) -> - Value; -validate_option(dhfile, Value) when is_list(Value), Value =/= "" -> - list_to_binary(Value); -validate_option(ciphers, Value) when is_list(Value) -> - Version = ssl_record:highest_protocol_version([]), - try cipher_suites(Version, Value) - catch - exit:_ -> - throw({error, {eoptions, {ciphers, Value}}}); - error:_-> - throw({error, {eoptions, {ciphers, Value}}}) - end; -validate_option(reuse_session, Value) when is_function(Value) -> - Value; -validate_option(reuse_sessions, Value) when Value == true; - Value == false -> - Value; - -validate_option(secure_renegotiate, Value) when Value == true; - Value == false -> - Value; -validate_option(renegotiate_at, Value) when is_integer(Value) -> - erlang:min(Value, ?DEFAULT_RENEGOTIATE_AT); - -validate_option(debug, Value) when is_list(Value); Value == true -> - Value; -validate_option(hibernate_after, undefined) -> - undefined; -validate_option(hibernate_after, Value) when is_integer(Value), Value >= 0 -> - Value; -validate_option(erl_dist,Value) when Value == true; - Value == false -> - Value; -validate_option(log_alert,Value) when Value == true; - Value == false -> - Value; -validate_option(Opt, Value) -> - throw({error, {eoptions, {Opt, Value}}}). - -validate_versions([], Versions) -> - Versions; -validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2'; - Version == 'tlsv1.1'; - Version == tlsv1; - Version == sslv3 -> - validate_versions(Rest, Versions); -validate_versions([Ver| _], Versions) -> - throw({error, {eoptions, {Ver, {versions, Versions}}}}). - -validate_inet_option(mode, Value) - when Value =/= list, Value =/= binary -> - throw({error, {eoptions, {mode,Value}}}); -validate_inet_option(packet, Value) - when not (is_atom(Value) orelse is_integer(Value)) -> - throw({error, {eoptions, {packet,Value}}}); -validate_inet_option(packet_size, Value) - when not is_integer(Value) -> - throw({error, {eoptions, {packet_size,Value}}}); -validate_inet_option(header, Value) - when not is_integer(Value) -> - throw({error, {eoptions, {header,Value}}}); -validate_inet_option(active, Value) - when Value =/= true, Value =/= false, Value =/= once -> - throw({error, {eoptions, {active,Value}}}); -validate_inet_option(_, _) -> - ok. - -%% The option cacerts overrides cacertsfile -ca_cert_default(_,_, [_|_]) -> - undefined; -ca_cert_default(verify_none, _, _) -> - undefined; -ca_cert_default(verify_peer, {Fun,_}, _) when is_function(Fun) -> - undefined; -%% Server that wants to verify_peer and has no verify_fun must have -%% some trusted certs. -ca_cert_default(verify_peer, undefined, _) -> - "". - -emulated_options() -> - [mode, packet, active, header, packet_size]. - -internal_inet_values() -> - [{packet_size,0},{packet, 0},{header, 0},{active, false},{mode,binary}]. - -socket_options(InetValues) -> - #socket_options{ - mode = proplists:get_value(mode, InetValues, lists), - header = proplists:get_value(header, InetValues, 0), - active = proplists:get_value(active, InetValues, active), - packet = proplists:get_value(packet, InetValues, 0), - packet_size = proplists:get_value(packet_size, InetValues) - }. - -emulated_options(Opts) -> - emulated_options(Opts, internal_inet_values(), #socket_options{}). - -emulated_options([{mode,Opt}|Opts], Inet, Emulated) -> - validate_inet_option(mode,Opt), - emulated_options(Opts, Inet, Emulated#socket_options{mode=Opt}); -emulated_options([{header,Opt}|Opts], Inet, Emulated) -> - validate_inet_option(header,Opt), - emulated_options(Opts, Inet, Emulated#socket_options{header=Opt}); -emulated_options([{active,Opt}|Opts], Inet, Emulated) -> - validate_inet_option(active,Opt), - emulated_options(Opts, Inet, Emulated#socket_options{active=Opt}); -emulated_options([{packet,Opt}|Opts], Inet, Emulated) -> - validate_inet_option(packet,Opt), - emulated_options(Opts, Inet, Emulated#socket_options{packet=Opt}); -emulated_options([{packet_size,Opt}|Opts], Inet, Emulated) -> - validate_inet_option(packet_size,Opt), - emulated_options(Opts, Inet, Emulated#socket_options{packet_size=Opt}); -emulated_options([Opt|Opts], Inet, Emulated) -> - emulated_options(Opts, [Opt|Inet], Emulated); -emulated_options([], Inet,Emulated) -> - {Inet, Emulated}. - -cipher_suites(Version, []) -> - ssl_cipher:suites(Version); -cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %% Backwards compatibility - Ciphers = [{KeyExchange, Cipher, Hash} || {KeyExchange, Cipher, Hash, _} <- Ciphers0], - cipher_suites(Version, Ciphers); -cipher_suites(Version, [{_,_,_}| _] = Ciphers0) -> - Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0], - cipher_suites(Version, Ciphers); -cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) -> - Supported = ssl_cipher:suites(Version) ++ ssl_cipher:anonymous_suites(), - case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported)] of - [] -> - Supported; - Ciphers -> - Ciphers - end; -cipher_suites(Version, [Head | _] = Ciphers0) when is_list(Head) -> - %% Format: ["RC4-SHA","RC4-MD5"] - Ciphers = [ssl_cipher:openssl_suite(C) || C <- Ciphers0], - cipher_suites(Version, Ciphers); -cipher_suites(Version, Ciphers0) -> - %% Format: "RC4-SHA:RC4-MD5" - Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:tokens(Ciphers0, ":")], - cipher_suites(Version, Ciphers). - -no_format(Error) -> - lists:flatten(io_lib:format("No format string for error: \"~p\" available.", [Error])). - -%% Only used to remove exit messages from old ssl -%% First is a nonsense clause to provide some -%% backward compatibility for orber that uses this -%% function in a none recommended way, but will -%% work correctly if a valid pid is returned. -%% Deprcated to be removed in r16 -pid(#sslsocket{fd = new_ssl}) -> - whereis(ssl_connection_sup); -pid(#sslsocket{pid = Pid}) -> - Pid. + tls:random_bytes(N). diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index 222b3f1ad7..1810043dfb 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -36,8 +36,7 @@ %% Internal application API %%==================================================================== %%-------------------------------------------------------------------- --spec reason_code(#alert{}, client | server) -> closed | esslconnect | - esslaccept | string(). +-spec reason_code(#alert{}, client | server) -> closed | {essl, string()}. %% %% Description: Returns the error reason that will be returned to the %% user. @@ -45,12 +44,8 @@ reason_code(#alert{description = ?CLOSE_NOTIFY}, _) -> closed; -reason_code(#alert{description = ?HANDSHAKE_FAILURE}, client) -> - esslconnect; -reason_code(#alert{description = ?HANDSHAKE_FAILURE}, server) -> - esslaccept; reason_code(#alert{description = Description}, _) -> - description_txt(Description). + {tls_alert, description_txt(Description)}. %%-------------------------------------------------------------------- -spec alert_txt(#alert{}) -> string(). @@ -117,4 +112,6 @@ description_txt(?INTERNAL_ERROR) -> description_txt(?USER_CANCELED) -> "user canceled"; description_txt(?NO_RENEGOTIATION) -> - "no renegotiation". + "no renegotiation"; +description_txt(?UNKNOWN_PSK_IDENTITY) -> + "unknown psk identity". diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl index 92548edab7..2a8a91aefa 100644 --- a/lib/ssl/src/ssl_alert.hrl +++ b/lib/ssl/src/ssl_alert.hrl @@ -60,6 +60,7 @@ %% internal_error(80), %% user_canceled(90), %% no_renegotiation(100), +%% unknown_psk_identity(115), %% (255) %% } AlertDescription; @@ -87,6 +88,7 @@ -define(INTERNAL_ERROR, 80). -define(USER_CANCELED, 90). -define(NO_RENEGOTIATION, 100). +-define(UNKNOWN_PSK_IDENTITY, 115). -define(ALERT_REC(Level,Desc), #alert{level=Level,description=Desc,where={?FILE, ?LINE}}). diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 86f5617b54..b186a1015a 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ is_valid_key_usage/2, select_extension/2, extensions_list/1, - signature_type/1 + public_key_type/1 ]). %%==================================================================== @@ -167,20 +167,16 @@ extensions_list(Extensions) -> Extensions. %%-------------------------------------------------------------------- --spec signature_type(term()) -> rsa | dsa . +-spec public_key_type(term()) -> rsa | dsa | ec. %% -%% Description: +%% Description: %%-------------------------------------------------------------------- -signature_type(RSA) when RSA == ?sha1WithRSAEncryption; - RSA == ?md5WithRSAEncryption; - RSA == ?sha224WithRSAEncryption; - RSA == ?sha256WithRSAEncryption; - RSA == ?sha384WithRSAEncryption; - RSA == ?sha512WithRSAEncryption - -> +public_key_type(?'rsaEncryption') -> rsa; -signature_type(?'id-dsa-with-sha1') -> - dsa. +public_key_type(?'id-dsa') -> + dsa; +public_key_type(?'id-ecPublicKey') -> + ec. %%-------------------------------------------------------------------- %%% Internal functions @@ -244,7 +240,7 @@ find_issuer(OtpCert, CertDbHandle) -> Acc end, - try ssl_certificate_db:foldl(IsIssuerFun, issuer_not_found, CertDbHandle) of + try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, CertDbHandle) of issuer_not_found -> {error, issuer_not_found} catch diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 567690a413..ec5d793d65 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -34,8 +34,8 @@ -export([security_parameters/3, suite_definition/1, decipher/5, cipher/5, - suite/1, suites/1, anonymous_suites/0, - openssl_suite/1, openssl_suite_name/1, filter/2, + suite/1, suites/1, anonymous_suites/0, psk_suites/1, srp_suites/0, + openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1, hash_algorithm/1, sign_algorithm/1]). -compile(inline). @@ -73,25 +73,25 @@ cipher(?NULL, CipherState, <<>>, Fragment, _Version) -> {GenStreamCipherList, CipherState}; cipher(?RC4, CipherState, Mac, Fragment, _Version) -> State0 = case CipherState#cipher_state.state of - undefined -> crypto:rc4_set_key(CipherState#cipher_state.key); + undefined -> crypto:stream_init(rc4, CipherState#cipher_state.key); S -> S end, GenStreamCipherList = [Fragment, Mac], - {State1, T} = crypto:rc4_encrypt_with_state(State0, GenStreamCipherList), + {State1, T} = crypto:stream_encrypt(State0, GenStreamCipherList), {T, CipherState#cipher_state{state = State1}}; cipher(?DES, CipherState, Mac, Fragment, Version) -> block_cipher(fun(Key, IV, T) -> - crypto:des_cbc_encrypt(Key, IV, T) + crypto:block_encrypt(des_cbc, Key, IV, T) end, block_size(des_cbc), CipherState, Mac, Fragment, Version); cipher(?'3DES', CipherState, Mac, Fragment, Version) -> block_cipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) -> - crypto:des3_cbc_encrypt(K1, K2, K3, IV, T) + crypto:block_encrypt(des3_cbc, [K1, K2, K3], IV, T) end, block_size(des_cbc), CipherState, Mac, Fragment, Version); cipher(?AES, CipherState, Mac, Fragment, Version) -> block_cipher(fun(Key, IV, T) when byte_size(Key) =:= 16 -> - crypto:aes_cbc_128_encrypt(Key, IV, T); + crypto:block_encrypt(aes_cbc128, Key, IV, T); (Key, IV, T) when byte_size(Key) =:= 32 -> - crypto:aes_cbc_256_encrypt(Key, IV, T) + crypto:block_encrypt(aes_cbc256, Key, IV, T) end, block_size(aes_128_cbc), CipherState, Mac, Fragment, Version). build_cipher_block(BlockSz, Mac, Fragment) -> @@ -127,10 +127,10 @@ decipher(?NULL, _HashSz, CipherState, Fragment, _) -> {Fragment, <<>>, CipherState}; decipher(?RC4, HashSz, CipherState, Fragment, _) -> State0 = case CipherState#cipher_state.state of - undefined -> crypto:rc4_set_key(CipherState#cipher_state.key); + undefined -> crypto:stream_init(rc4, CipherState#cipher_state.key); S -> S end, - try crypto:rc4_encrypt_with_state(State0, Fragment) of + try crypto:stream_decrypt(State0, Fragment) of {State, Text} -> GSC = generic_stream_cipher_from_bin(Text, HashSz), #generic_stream_cipher{content = Content, mac = Mac} = GSC, @@ -147,17 +147,17 @@ decipher(?RC4, HashSz, CipherState, Fragment, _) -> decipher(?DES, HashSz, CipherState, Fragment, Version) -> block_decipher(fun(Key, IV, T) -> - crypto:des_cbc_decrypt(Key, IV, T) + crypto:block_decrypt(des_cbc, Key, IV, T) end, CipherState, HashSz, Fragment, Version); decipher(?'3DES', HashSz, CipherState, Fragment, Version) -> block_decipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) -> - crypto:des3_cbc_decrypt(K1, K2, K3, IV, T) + crypto:block_decrypt(des3_cbc, [K1, K2, K3], IV, T) end, CipherState, HashSz, Fragment, Version); decipher(?AES, HashSz, CipherState, Fragment, Version) -> block_decipher(fun(Key, IV, T) when byte_size(Key) =:= 16 -> - crypto:aes_cbc_128_decrypt(Key, IV, T); + crypto:block_decrypt(aes_cbc128, Key, IV, T); (Key, IV, T) when byte_size(Key) =:= 32 -> - crypto:aes_cbc_256_decrypt(Key, IV, T) + crypto:block_decrypt(aes_cbc256, Key, IV, T) end, CipherState, HashSz, Fragment, Version). block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, @@ -212,7 +212,61 @@ anonymous_suites() -> ?TLS_DH_anon_WITH_AES_128_CBC_SHA, ?TLS_DH_anon_WITH_AES_256_CBC_SHA, ?TLS_DH_anon_WITH_AES_128_CBC_SHA256, - ?TLS_DH_anon_WITH_AES_256_CBC_SHA256]. + ?TLS_DH_anon_WITH_AES_256_CBC_SHA256, + ?TLS_ECDH_anon_WITH_RC4_128_SHA, + ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDH_anon_WITH_AES_128_CBC_SHA, + ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA]. + +%%-------------------------------------------------------------------- +-spec psk_suites(tls_version() | integer()) -> [cipher_suite()]. +%% +%% Description: Returns a list of the PSK cipher suites, only supported +%% if explicitly set by user. +%%-------------------------------------------------------------------- +psk_suites({3, N}) -> + psk_suites(N); + +psk_suites(N) + when N >= 3 -> + psk_suites(0) ++ + [?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, + ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, + ?TLS_PSK_WITH_AES_256_CBC_SHA384, + ?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, + ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, + ?TLS_PSK_WITH_AES_128_CBC_SHA256]; + +psk_suites(_) -> + [?TLS_DHE_PSK_WITH_AES_256_CBC_SHA, + ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA, + ?TLS_PSK_WITH_AES_256_CBC_SHA, + ?TLS_DHE_PSK_WITH_AES_128_CBC_SHA, + ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA, + ?TLS_PSK_WITH_AES_128_CBC_SHA, + ?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, + ?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, + ?TLS_PSK_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_PSK_WITH_RC4_128_SHA, + ?TLS_RSA_PSK_WITH_RC4_128_SHA, + ?TLS_PSK_WITH_RC4_128_SHA]. + +%%-------------------------------------------------------------------- +-spec srp_suites() -> [cipher_suite()]. +%% +%% Description: Returns a list of the SRP cipher suites, only supported +%% if explicitly set by user. +%%-------------------------------------------------------------------- +srp_suites() -> + [?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA, + ?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_SRP_SHA_WITH_AES_128_CBC_SHA, + ?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, + ?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, + ?TLS_SRP_SHA_WITH_AES_256_CBC_SHA, + ?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA, + ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA]. %%-------------------------------------------------------------------- -spec suite_definition(cipher_suite()) -> int_cipher_suite(). @@ -224,6 +278,11 @@ anonymous_suites() -> %% TLS v1.1 suites suite_definition(?TLS_NULL_WITH_NULL_NULL) -> {null, null, null, null}; +%% RFC 5746 - Not a real cipher suite used to signal empty "renegotiation_info" extension +%% to avoid handshake failure from old servers that do not ignore +%% hello extension data as they should. +suite_definition(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV) -> + {null, null, null, null}; %% suite_definition(?TLS_RSA_WITH_NULL_MD5) -> %% {rsa, null, md5, default_prf}; %% suite_definition(?TLS_RSA_WITH_NULL_SHA) -> @@ -297,7 +356,157 @@ suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) -> suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA256) -> {dh_anon, aes_128_cbc, sha256, default_prf}; suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA256) -> - {dh_anon, aes_256_cbc, sha256, default_prf}. + {dh_anon, aes_256_cbc, sha256, default_prf}; + +%%% PSK Cipher Suites RFC 4279 + +suite_definition(?TLS_PSK_WITH_RC4_128_SHA) -> + {psk, rc4_128, sha, default_prf}; +suite_definition(?TLS_PSK_WITH_3DES_EDE_CBC_SHA) -> + {psk, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_PSK_WITH_AES_128_CBC_SHA) -> + {psk, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_PSK_WITH_AES_256_CBC_SHA) -> + {psk, aes_256_cbc, sha, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_RC4_128_SHA) -> + {dhe_psk, rc4_128, sha, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA) -> + {dhe_psk, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_AES_128_CBC_SHA) -> + {dhe_psk, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_AES_256_CBC_SHA) -> + {dhe_psk, aes_256_cbc, sha, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_RC4_128_SHA) -> + {rsa_psk, rc4_128, sha, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA) -> + {rsa_psk, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA) -> + {rsa_psk, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA) -> + {rsa_psk, aes_256_cbc, sha, default_prf}; + +%%% TLS 1.2 PSK Cipher Suites RFC 5487 + +suite_definition(?TLS_PSK_WITH_AES_128_CBC_SHA256) -> + {psk, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_PSK_WITH_AES_256_CBC_SHA384) -> + {psk, aes_256_cbc, sha384, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256) -> + {dhe_psk, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384) -> + {dhe_psk, aes_256_cbc, sha384, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256) -> + {rsa_psk, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384) -> + {rsa_psk, aes_256_cbc, sha384, default_prf}; + +suite_definition(?TLS_PSK_WITH_NULL_SHA256) -> + {psk, null, sha256, default_prf}; +suite_definition(?TLS_PSK_WITH_NULL_SHA384) -> + {psk, null, sha384, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_NULL_SHA256) -> + {dhe_psk, null, sha256, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_NULL_SHA384) -> + {dhe_psk, null, sha384, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA256) -> + {rsa_psk, null, sha256, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA384) -> + {rsa_psk, null, sha384, default_prf}; + +%%% SRP Cipher Suites RFC 5054 + +suite_definition(?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) -> + {srp_anon, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) -> + {srp_rsa, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA) -> + {srp_dss, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_SRP_SHA_WITH_AES_128_CBC_SHA) -> + {srp_anon, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) -> + {srp_rsa, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA) -> + {srp_dss, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_SRP_SHA_WITH_AES_256_CBC_SHA) -> + {srp_anon, aes_256_cbc, sha, default_prf}; +suite_definition(?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) -> + {srp_rsa, aes_256_cbc, sha, default_prf}; +suite_definition(?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA) -> + {srp_dss, aes_256_cbc, sha, default_prf}; + +%% RFC 4492 EC TLS suites +suite_definition(?TLS_ECDH_ECDSA_WITH_NULL_SHA) -> + {ecdh_ecdsa, null, sha, default_prf}; +suite_definition(?TLS_ECDH_ECDSA_WITH_RC4_128_SHA) -> + {ecdh_ecdsa, rc4_128, sha, default_prf}; +suite_definition(?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA) -> + {ecdh_ecdsa, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA) -> + {ecdh_ecdsa, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA) -> + {ecdh_ecdsa, aes_256_cbc, sha, default_prf}; + +suite_definition(?TLS_ECDHE_ECDSA_WITH_NULL_SHA) -> + {ecdhe_ecdsa, null, sha, default_prf}; +suite_definition(?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA) -> + {ecdhe_ecdsa, rc4_128, sha, default_prf}; +suite_definition(?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA) -> + {ecdhe_ecdsa, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) -> + {ecdhe_ecdsa, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) -> + {ecdhe_ecdsa, aes_256_cbc, sha, default_prf}; + +suite_definition(?TLS_ECDH_RSA_WITH_NULL_SHA) -> + {ecdh_rsa, null, sha, default_prf}; +suite_definition(?TLS_ECDH_RSA_WITH_RC4_128_SHA) -> + {ecdh_rsa, rc4_128, sha, default_prf}; +suite_definition(?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA) -> + {ecdh_rsa, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA) -> + {ecdh_rsa, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA) -> + {ecdh_rsa, aes_256_cbc, sha, default_prf}; + +suite_definition(?TLS_ECDHE_RSA_WITH_NULL_SHA) -> + {ecdhe_rsa, null, sha, default_prf}; +suite_definition(?TLS_ECDHE_RSA_WITH_RC4_128_SHA) -> + {ecdhe_rsa, rc4_128, sha, default_prf}; +suite_definition(?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA) -> + {ecdhe_rsa, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA) -> + {ecdhe_rsa, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) -> + {ecdhe_rsa, aes_256_cbc, sha, default_prf}; + +suite_definition(?TLS_ECDH_anon_WITH_NULL_SHA) -> + {ecdh_anon, null, sha, default_prf}; +suite_definition(?TLS_ECDH_anon_WITH_RC4_128_SHA) -> + {ecdh_anon, rc4_128, sha, default_prf}; +suite_definition(?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA) -> + {ecdh_anon, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_ECDH_anon_WITH_AES_128_CBC_SHA) -> + {ecdh_anon, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_ECDH_anon_WITH_AES_256_CBC_SHA) -> + {ecdh_anon, aes_256_cbc, sha, default_prf}; + +%% RFC 5289 EC TLS suites +suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256) -> + {ecdhe_ecdsa, aes_128_cbc, sha256, sha256}; +suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384) -> + {ecdhe_ecdsa, aes_256_cbc, sha384, sha384}; +suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256) -> + {ecdh_ecdsa, aes_128_cbc, sha256, sha256}; +suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384) -> + {ecdh_ecdsa, aes_256_cbc, sha384, sha384}; +suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) -> + {ecdhe_rsa, aes_128_cbc, sha256, sha256}; +suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) -> + {ecdhe_rsa, aes_256_cbc, sha384, sha384}; +suite_definition(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256) -> + {ecdh_rsa, aes_128_cbc, sha256, sha256}; +suite_definition(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) -> + {ecdh_rsa, aes_256_cbc, sha384, sha384}. %%-------------------------------------------------------------------- -spec suite(erl_cipher_suite()) -> cipher_suite(). @@ -370,7 +579,157 @@ suite({dhe_rsa, aes_256_cbc, sha256}) -> suite({dh_anon, aes_128_cbc, sha256}) -> ?TLS_DH_anon_WITH_AES_128_CBC_SHA256; suite({dh_anon, aes_256_cbc, sha256}) -> - ?TLS_DH_anon_WITH_AES_256_CBC_SHA256. + ?TLS_DH_anon_WITH_AES_256_CBC_SHA256; + +%%% PSK Cipher Suites RFC 4279 + +suite({psk, rc4_128,sha}) -> + ?TLS_PSK_WITH_RC4_128_SHA; +suite({psk, '3des_ede_cbc',sha}) -> + ?TLS_PSK_WITH_3DES_EDE_CBC_SHA; +suite({psk, aes_128_cbc,sha}) -> + ?TLS_PSK_WITH_AES_128_CBC_SHA; +suite({psk, aes_256_cbc,sha}) -> + ?TLS_PSK_WITH_AES_256_CBC_SHA; +suite({dhe_psk, rc4_128,sha}) -> + ?TLS_DHE_PSK_WITH_RC4_128_SHA; +suite({dhe_psk, '3des_ede_cbc',sha}) -> + ?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA; +suite({dhe_psk, aes_128_cbc,sha}) -> + ?TLS_DHE_PSK_WITH_AES_128_CBC_SHA; +suite({dhe_psk, aes_256_cbc,sha}) -> + ?TLS_DHE_PSK_WITH_AES_256_CBC_SHA; +suite({rsa_psk, rc4_128,sha}) -> + ?TLS_RSA_PSK_WITH_RC4_128_SHA; +suite({rsa_psk, '3des_ede_cbc',sha}) -> + ?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA; +suite({rsa_psk, aes_128_cbc,sha}) -> + ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA; +suite({rsa_psk, aes_256_cbc,sha}) -> + ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA; + +%%% TLS 1.2 PSK Cipher Suites RFC 5487 + +suite({psk, aes_128_cbc, sha256}) -> + ?TLS_PSK_WITH_AES_128_CBC_SHA256; +suite({psk, aes_256_cbc, sha384}) -> + ?TLS_PSK_WITH_AES_256_CBC_SHA384; +suite({dhe_psk, aes_128_cbc, sha256}) -> + ?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256; +suite({dhe_psk, aes_256_cbc, sha384}) -> + ?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384; +suite({rsa_psk, aes_128_cbc, sha256}) -> + ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256; +suite({rsa_psk, aes_256_cbc, sha384}) -> + ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384; + +suite({psk, null, sha256}) -> + ?TLS_PSK_WITH_NULL_SHA256; +suite({psk, null, sha384}) -> + ?TLS_PSK_WITH_NULL_SHA384; +suite({dhe_psk, null, sha256}) -> + ?TLS_DHE_PSK_WITH_NULL_SHA256; +suite({dhe_psk, null, sha384}) -> + ?TLS_DHE_PSK_WITH_NULL_SHA384; +suite({rsa_psk, null, sha256}) -> + ?TLS_RSA_PSK_WITH_NULL_SHA256; +suite({rsa_psk, null, sha384}) -> + ?TLS_RSA_PSK_WITH_NULL_SHA384; + +%%% SRP Cipher Suites RFC 5054 + +suite({srp_anon, '3des_ede_cbc', sha}) -> + ?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA; +suite({srp_rsa, '3des_ede_cbc', sha}) -> + ?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA; +suite({srp_dss, '3des_ede_cbc', sha}) -> + ?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA; +suite({srp_anon, aes_128_cbc, sha}) -> + ?TLS_SRP_SHA_WITH_AES_128_CBC_SHA; +suite({srp_rsa, aes_128_cbc, sha}) -> + ?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA; +suite({srp_dss, aes_128_cbc, sha}) -> + ?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA; +suite({srp_anon, aes_256_cbc, sha}) -> + ?TLS_SRP_SHA_WITH_AES_256_CBC_SHA; +suite({srp_rsa, aes_256_cbc, sha}) -> + ?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA; +suite({srp_dss, aes_256_cbc, sha}) -> + ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA; + +%%% RFC 4492 EC TLS suites +suite({ecdh_ecdsa, null, sha}) -> + ?TLS_ECDH_ECDSA_WITH_NULL_SHA; +suite({ecdh_ecdsa, rc4_128, sha}) -> + ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA; +suite({ecdh_ecdsa, '3des_ede_cbc', sha}) -> + ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA; +suite({ecdh_ecdsa, aes_128_cbc, sha}) -> + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA; +suite({ecdh_ecdsa, aes_256_cbc, sha}) -> + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA; + +suite({ecdhe_ecdsa, null, sha}) -> + ?TLS_ECDHE_ECDSA_WITH_NULL_SHA; +suite({ecdhe_ecdsa, rc4_128, sha}) -> + ?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA; +suite({ecdhe_ecdsa, '3des_ede_cbc', sha}) -> + ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA; +suite({ecdhe_ecdsa, aes_128_cbc, sha}) -> + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA; +suite({ecdhe_ecdsa, aes_256_cbc, sha}) -> + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA; + +suite({ecdh_rsa, null, sha}) -> + ?TLS_ECDH_RSA_WITH_NULL_SHA; +suite({ecdh_rsa, rc4_128, sha}) -> + ?TLS_ECDH_RSA_WITH_RC4_128_SHA; +suite({ecdh_rsa, '3des_ede_cbc', sha}) -> + ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA; +suite({ecdh_rsa, aes_128_cbc, sha}) -> + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA; +suite({ecdh_rsa, aes_256_cbc, sha}) -> + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA; + +suite({ecdhe_rsa, null, sha}) -> + ?TLS_ECDHE_RSA_WITH_NULL_SHA; +suite({ecdhe_rsa, rc4_128, sha}) -> + ?TLS_ECDHE_RSA_WITH_RC4_128_SHA; +suite({ecdhe_rsa, '3des_ede_cbc', sha}) -> + ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA; +suite({ecdhe_rsa, aes_128_cbc, sha}) -> + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA; +suite({ecdhe_rsa, aes_256_cbc, sha}) -> + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA; + +suite({ecdh_anon, null, sha}) -> + ?TLS_ECDH_anon_WITH_NULL_SHA; +suite({ecdh_anon, rc4_128, sha}) -> + ?TLS_ECDH_anon_WITH_RC4_128_SHA; +suite({ecdh_anon, '3des_ede_cbc', sha}) -> + ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA; +suite({ecdh_anon, aes_128_cbc, sha}) -> + ?TLS_ECDH_anon_WITH_AES_128_CBC_SHA; +suite({ecdh_anon, aes_256_cbc, sha}) -> + ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA; + +%%% RFC 5289 EC TLS suites +suite({ecdhe_ecdsa, aes_128_cbc, sha256}) -> + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; +suite({ecdhe_ecdsa, aes_256_cbc, sha384}) -> + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384; +suite({ecdh_ecdsa, aes_128_cbc, sha256}) -> + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256; +suite({ecdh_ecdsa, aes_256_cbc, sha384}) -> + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384; +suite({ecdhe_rsa, aes_128_cbc, sha256}) -> + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256; +suite({ecdhe_rsa, aes_256_cbc, sha384}) -> + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384; +suite({ecdh_rsa, aes_128_cbc, sha256}) -> + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256; +suite({ecdh_rsa, aes_256_cbc, sha384}) -> + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384. %%-------------------------------------------------------------------- -spec openssl_suite(openssl_cipher_suite()) -> cipher_suite(). @@ -415,7 +774,78 @@ openssl_suite("RC4-MD5") -> openssl_suite("EDH-RSA-DES-CBC-SHA") -> ?TLS_DHE_RSA_WITH_DES_CBC_SHA; openssl_suite("DES-CBC-SHA") -> - ?TLS_RSA_WITH_DES_CBC_SHA. + ?TLS_RSA_WITH_DES_CBC_SHA; + +%%% SRP Cipher Suites RFC 5054 + +openssl_suite("SRP-DSS-AES-256-CBC-SHA") -> + ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA; +openssl_suite("SRP-RSA-AES-256-CBC-SHA") -> + ?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA; +openssl_suite("SRP-DSS-3DES-EDE-CBC-SHA") -> + ?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA; +openssl_suite("SRP-RSA-3DES-EDE-CBC-SHA") -> + ?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA; +openssl_suite("SRP-DSS-AES-128-CBC-SHA") -> + ?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA; +openssl_suite("SRP-RSA-AES-128-CBC-SHA") -> + ?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA; + +%% RFC 4492 EC TLS suites +openssl_suite("ECDH-ECDSA-RC4-SHA") -> + ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA; +openssl_suite("ECDH-ECDSA-DES-CBC3-SHA") -> + ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA; +openssl_suite("ECDH-ECDSA-AES128-SHA") -> + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA; +openssl_suite("ECDH-ECDSA-AES256-SHA") -> + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA; + +openssl_suite("ECDHE-ECDSA-RC4-SHA") -> + ?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA; +openssl_suite("ECDHE-ECDSA-DES-CBC3-SHA") -> + ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA; +openssl_suite("ECDHE-ECDSA-AES128-SHA") -> + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA; +openssl_suite("ECDHE-ECDSA-AES256-SHA") -> + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA; + +openssl_suite("ECDHE-RSA-RC4-SHA") -> + ?TLS_ECDHE_RSA_WITH_RC4_128_SHA; +openssl_suite("ECDHE-RSA-DES-CBC3-SHA") -> + ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA; +openssl_suite("ECDHE-RSA-AES128-SHA") -> + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA; +openssl_suite("ECDHE-RSA-AES256-SHA") -> + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA; + +openssl_suite("ECDH-RSA-RC4-SHA") -> + ?TLS_ECDH_RSA_WITH_RC4_128_SHA; +openssl_suite("ECDH-RSA-DES-CBC3-SHA") -> + ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA; +openssl_suite("ECDH-RSA-AES128-SHA") -> + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA; +openssl_suite("ECDH-RSA-AES256-SHA") -> + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA; + +%% RFC 5289 EC TLS suites +openssl_suite("ECDHE-ECDSA-AES128-SHA256") -> + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; +openssl_suite("ECDHE-ECDSA-AES256-SHA384") -> + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384; +openssl_suite("ECDH-ECDSA-AES128-SHA256") -> + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256; +openssl_suite("ECDH-ECDSA-AES256-SHA384") -> + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384; +openssl_suite("ECDHE-RSA-AES128-SHA256") -> + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256; +openssl_suite("ECDHE-RSA-AES256-SHA384") -> + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384; +openssl_suite("ECDH-RSA-AES128-SHA256") -> + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256; +openssl_suite("ECDH-RSA-AES256-SHA384") -> + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384. + %%-------------------------------------------------------------------- -spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite(). %% @@ -469,6 +899,88 @@ openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) -> "DHE-DSS-AES256-SHA256"; openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) -> "DHE-RSA-AES256-SHA256"; + +%%% PSK Cipher Suites RFC 4279 + +openssl_suite_name(?TLS_PSK_WITH_AES_256_CBC_SHA) -> + "PSK-AES256-CBC-SHA"; +openssl_suite_name(?TLS_PSK_WITH_3DES_EDE_CBC_SHA) -> + "PSK-3DES-EDE-CBC-SHA"; +openssl_suite_name(?TLS_PSK_WITH_AES_128_CBC_SHA) -> + "PSK-AES128-CBC-SHA"; +openssl_suite_name(?TLS_PSK_WITH_RC4_128_SHA) -> + "PSK-RC4-SHA"; + +%%% SRP Cipher Suites RFC 5054 + +openssl_suite_name(?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) -> + "SRP-RSA-3DES-EDE-CBC-SHA"; +openssl_suite_name(?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA) -> + "SRP-DSS-3DES-EDE-CBC-SHA"; +openssl_suite_name(?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) -> + "SRP-RSA-AES-128-CBC-SHA"; +openssl_suite_name(?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA) -> + "SRP-DSS-AES-128-CBC-SHA"; +openssl_suite_name(?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) -> + "SRP-RSA-AES-256-CBC-SHA"; +openssl_suite_name(?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA) -> + "SRP-DSS-AES-256-CBC-SHA"; + +%% RFC 4492 EC TLS suites +openssl_suite_name(?TLS_ECDH_ECDSA_WITH_RC4_128_SHA) -> + "ECDH-ECDSA-RC4-SHA"; +openssl_suite_name(?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA) -> + "ECDH-ECDSA-DES-CBC3-SHA"; +openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA) -> + "ECDH-ECDSA-AES128-SHA"; +openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA) -> + "ECDH-ECDSA-AES256-SHA"; + +openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA) -> + "ECDHE-ECDSA-RC4-SHA"; +openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA) -> + "ECDHE-ECDSA-DES-CBC3-SHA"; +openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) -> + "ECDHE-ECDSA-AES128-SHA"; +openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) -> + "ECDHE-ECDSA-AES256-SHA"; + +openssl_suite_name(?TLS_ECDH_RSA_WITH_RC4_128_SHA) -> + "ECDH-RSA-RC4-SHA"; +openssl_suite_name(?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA) -> + "ECDH-RSA-DES-CBC3-SHA"; +openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA) -> + "ECDH-RSA-AES128-SHA"; +openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA) -> + "ECDH-RSA-AES256-SHA"; + +openssl_suite_name(?TLS_ECDHE_RSA_WITH_RC4_128_SHA) -> + "ECDHE-RSA-RC4-SHA"; +openssl_suite_name(?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA) -> + "ECDHE-RSA-DES-CBC3-SHA"; +openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA) -> + "ECDHE-RSA-AES128-SHA"; +openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) -> + "ECDHE-RSA-AES256-SHA"; + +%% RFC 5289 EC TLS suites +openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256) -> + "ECDHE-ECDSA-AES128-SHA256"; +openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384) -> + "ECDHE-ECDSA-AES256-SHA384"; +openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256) -> + "ECDH-ECDSA-AES128-SHA256"; +openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384) -> + "ECDH-ECDSA-AES256-SHA384"; +openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) -> + "ECDHE-RSA-AES128-SHA256"; +openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) -> + "ECDHE-RSA-AES256-SHA384"; +openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256) -> + "ECDH-RSA-AES128-SHA256"; +openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) -> + "ECDH-RSA-AES256-SHA384"; + %% No oppenssl name openssl_suite_name(Cipher) -> suite_definition(Cipher). @@ -483,14 +995,87 @@ filter(undefined, Ciphers) -> filter(DerCert, Ciphers) -> OtpCert = public_key:pkix_decode_cert(DerCert, otp), SigAlg = OtpCert#'OTPCertificate'.signatureAlgorithm, - case ssl_certificate:signature_type(SigAlg#'SignatureAlgorithm'.algorithm) of - rsa -> - filter_rsa(OtpCert, Ciphers -- dsa_signed_suites()); - dsa -> - Ciphers -- rsa_signed_suites() + PubKeyInfo = OtpCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subjectPublicKeyInfo, + PubKeyAlg = PubKeyInfo#'OTPSubjectPublicKeyInfo'.algorithm, + + Ciphers1 = + case ssl_certificate:public_key_type(PubKeyAlg#'PublicKeyAlgorithm'.algorithm) of + rsa -> + filter_keyuse(OtpCert, ((Ciphers -- dsa_signed_suites()) -- ec_keyed_suites()) -- ecdh_suites(), + rsa_suites(), dhe_rsa_suites() ++ ecdhe_rsa_suites()); + dsa -> + (Ciphers -- rsa_keyed_suites()) -- ec_keyed_suites(); + ec -> + filter_keyuse(OtpCert, (Ciphers -- rsa_keyed_suites()) -- dsa_signed_suites(), + [], ecdhe_ecdsa_suites()) + end, + case public_key:pkix_sign_types(SigAlg#'SignatureAlgorithm'.algorithm) of + {_, rsa} -> + Ciphers1 -- ecdsa_signed_suites(); + {_, dsa} -> + Ciphers1; + {_, ecdsa} -> + Ciphers1 -- rsa_signed_suites() end. %%-------------------------------------------------------------------- +-spec filter_suites([cipher_suite()]) -> [cipher_suite()]. +%% +%% Description: filter suites for algorithms +%%------------------------------------------------------------------- +filter_suites(Suites = [{_,_,_}|_]) -> + Algos = crypto:supports(), + lists:filter(fun({KeyExchange, Cipher, Hash}) -> + is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso + is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso + is_acceptable_hash(Hash, proplists:get_value(hashs, Algos)) + end, Suites); + +filter_suites(Suites = [{_,_,_,_}|_]) -> + Algos = crypto:supports(), + Hashs = proplists:get_value(hashs, Algos), + lists:filter(fun({KeyExchange, Cipher, Hash, Prf}) -> + is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso + is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso + is_acceptable_hash(Hash, Hashs) andalso + is_acceptable_prf(Prf, Hashs) + end, Suites); + +filter_suites(Suites) -> + Algos = crypto:supports(), + Hashs = proplists:get_value(hashs, Algos), + lists:filter(fun(Suite) -> + {KeyExchange, Cipher, Hash, Prf} = ssl_cipher:suite_definition(Suite), + is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso + is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso + is_acceptable_hash(Hash, Hashs) andalso + is_acceptable_prf(Prf, Hashs) + end, Suites). + +is_acceptable_keyexchange(KeyExchange, Algos) + when KeyExchange == ecdh_ecdsa; + KeyExchange == ecdhe_ecdsa; + KeyExchange == ecdh_rsa; + KeyExchange == ecdhe_rsa; + KeyExchange == ecdh_anon -> + proplists:get_bool(ecdh, Algos); +is_acceptable_keyexchange(_, _) -> + true. + +is_acceptable_cipher(_, _) -> + true. + +is_acceptable_hash(null, _Algos) -> + true; +is_acceptable_hash(Hash, Algos) -> + proplists:get_bool(Hash, Algos). + +is_acceptable_prf(default_prf, _) -> + true; +is_acceptable_prf(Prf, Algos) -> + proplists:get_bool(Prf, Algos). + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -605,12 +1190,14 @@ hash_size(md5) -> 16; hash_size(sha) -> 20; +%% Uncomment when adding cipher suite that needs it +%% hash_size(sha224) -> +%% 28; hash_size(sha256) -> - 32. -%% Currently no supported cipher suites defaults to sha384 or sha512 -%% so these clauses are not needed at the moment. -%% hash_size(sha384) -> -%% 48; + 32; +hash_size(sha384) -> + 48. +%% Uncomment when adding cipher suite that needs it %% hash_size(sha512) -> %% 64. @@ -702,7 +1289,14 @@ next_iv(Bin, IV) -> NextIV. rsa_signed_suites() -> - dhe_rsa_suites() ++ rsa_suites(). + dhe_rsa_suites() ++ rsa_suites() ++ + psk_rsa_suites() ++ srp_rsa_suites() ++ + ecdh_rsa_suites(). + +rsa_keyed_suites() -> + dhe_rsa_suites() ++ rsa_suites() ++ + psk_rsa_suites() ++ srp_rsa_suites() ++ + ecdhe_rsa_suites(). dhe_rsa_suites() -> [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, @@ -712,6 +1306,19 @@ dhe_rsa_suites() -> ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, ?TLS_DHE_RSA_WITH_DES_CBC_SHA]. +psk_rsa_suites() -> + [?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, + ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, + ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA, + ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA, + ?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, + ?TLS_RSA_PSK_WITH_RC4_128_SHA]. + +srp_rsa_suites() -> + [?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, + ?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA]. + rsa_suites() -> [?TLS_RSA_WITH_AES_256_CBC_SHA256, ?TLS_RSA_WITH_AES_256_CBC_SHA, @@ -721,9 +1328,27 @@ rsa_suites() -> ?TLS_RSA_WITH_RC4_128_SHA, ?TLS_RSA_WITH_RC4_128_MD5, ?TLS_RSA_WITH_DES_CBC_SHA]. - + +ecdh_rsa_suites() -> + [?TLS_ECDH_RSA_WITH_NULL_SHA, + ?TLS_ECDH_RSA_WITH_RC4_128_SHA, + ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384]. + +ecdhe_rsa_suites() -> + [?TLS_ECDHE_RSA_WITH_NULL_SHA, + ?TLS_ECDHE_RSA_WITH_RC4_128_SHA, + ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384]. + dsa_signed_suites() -> - dhe_dss_suites(). + dhe_dss_suites() ++ srp_dss_suites(). dhe_dss_suites() -> [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, @@ -733,24 +1358,57 @@ dhe_dss_suites() -> ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA]. -filter_rsa(OtpCert, RsaCiphers) -> +srp_dss_suites() -> + [?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, + ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA]. + +ec_keyed_suites() -> + ecdh_ecdsa_suites() ++ ecdhe_ecdsa_suites() + ++ ecdh_rsa_suites(). + +ecdsa_signed_suites() -> + ecdh_ecdsa_suites() ++ ecdhe_ecdsa_suites(). + +ecdh_suites() -> + ecdh_rsa_suites() ++ ecdh_ecdsa_suites(). + +ecdh_ecdsa_suites() -> + [?TLS_ECDH_ECDSA_WITH_NULL_SHA, + ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384]. + +ecdhe_ecdsa_suites() -> + [?TLS_ECDHE_ECDSA_WITH_NULL_SHA, + ?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384]. + +filter_keyuse(OtpCert, Ciphers, Suites, SignSuites) -> TBSCert = OtpCert#'OTPCertificate'.tbsCertificate, TBSExtensions = TBSCert#'OTPTBSCertificate'.extensions, Extensions = ssl_certificate:extensions_list(TBSExtensions), case ssl_certificate:select_extension(?'id-ce-keyUsage', Extensions) of undefined -> - RsaCiphers; + Ciphers; #'Extension'{extnValue = KeyUse} -> - Result = filter_rsa_suites(keyEncipherment, - KeyUse, RsaCiphers, rsa_suites()), - filter_rsa_suites(digitalSignature, - KeyUse, Result, dhe_rsa_suites()) + Result = filter_keyuse_suites(keyEncipherment, + KeyUse, Ciphers, Suites), + filter_keyuse_suites(digitalSignature, + KeyUse, Result, SignSuites) end. -filter_rsa_suites(Use, KeyUse, CipherSuits, RsaSuites) -> +filter_keyuse_suites(Use, KeyUse, CipherSuits, Suites) -> case ssl_certificate:is_valid_key_usage(KeyUse, Use) of true -> CipherSuits; false -> - CipherSuits -- RsaSuites + CipherSuits -- Suites end. diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl index 0f439f8ed5..c7c71ee1a7 100644 --- a/lib/ssl/src/ssl_cipher.hrl +++ b/lib/ssl/src/ssl_cipher.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,9 +28,9 @@ -type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc' | aes_128_cbc | aes_256_cbc. --type hash() :: null | sha | md5 | sha256 | sha384 | sha512. +-type hash() :: null | sha | md5 | ssh224 | sha256 | sha384 | sha512. -type erl_cipher_suite() :: {key_algo(), cipher(), hash()}. --type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash()}. +-type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash() | default_prf}. -type cipher_suite() :: binary(). -type cipher_enum() :: integer(). -type openssl_cipher_suite() :: string(). @@ -219,6 +219,120 @@ %% TLS_DH_anon_WITH_AES_256_CBC_SHA256 = { 0x00,0x6D }; -define(TLS_DH_anon_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6D)>>). +%% RFC 4492 EC TLS suites + +%% ECDH_ECDSA + +%% TLS_ECDH_ECDSA_WITH_NULL_SHA = { 0xC0, 0x01 } +-define(TLS_ECDH_ECDSA_WITH_NULL_SHA, <<?BYTE(16#C0), ?BYTE(16#01)>>). + +%% TLS_ECDH_ECDSA_WITH_RC4_128_SHA = { 0xC0, 0x02 } +-define(TLS_ECDH_ECDSA_WITH_RC4_128_SHA, <<?BYTE(16#C0), ?BYTE(16#02)>>). + +%% TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = { 0xC0, 0x03 } +-define(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#03)>>). + +%% TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = { 0xC0, 0x04 } +-define(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#04)>>). + +%% TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = { 0xC0, 0x05 } +-define(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#05)>>). + +%% ECDHE_ECDSA + +%% TLS_ECDHE_ECDSA_WITH_NULL_SHA = { 0xC0, 0x06 } +-define(TLS_ECDHE_ECDSA_WITH_NULL_SHA, <<?BYTE(16#C0), ?BYTE(16#06)>>). + +%% TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = { 0xC0, 0x07 } +-define(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, <<?BYTE(16#C0), ?BYTE(16#07)>>). + +%% TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = { 0xC0, 0x08 } +-define(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#08)>>). + +%% TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = { 0xC0, 0x09 } +-define(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#09)>>). + +%% TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = { 0xC0, 0x0A } +-define(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#0A)>>). + +%% ECDH_RSA + +%% TLS_ECDH_RSA_WITH_NULL_SHA = { 0xC0, 0x0B } +-define(TLS_ECDH_RSA_WITH_NULL_SHA, <<?BYTE(16#C0), ?BYTE(16#0B)>>). + +%% TLS_ECDH_RSA_WITH_RC4_128_SHA = { 0xC0, 0x0C } +-define(TLS_ECDH_RSA_WITH_RC4_128_SHA, <<?BYTE(16#C0), ?BYTE(16#0C)>>). + +%% TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = { 0xC0, 0x0D } +-define(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#0D)>>). + +%% TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = { 0xC0, 0x0E } +-define(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#0E)>>). + +%% TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = { 0xC0, 0x0F } +-define(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#0F)>>). + +%% ECDHE_RSA + +%% TLS_ECDHE_RSA_WITH_NULL_SHA = { 0xC0, 0x10 } +-define(TLS_ECDHE_RSA_WITH_NULL_SHA, <<?BYTE(16#C0), ?BYTE(16#10)>>). + +%% TLS_ECDHE_RSA_WITH_RC4_128_SHA = { 0xC0, 0x11 } +-define(TLS_ECDHE_RSA_WITH_RC4_128_SHA, <<?BYTE(16#C0), ?BYTE(16#11)>>). + +%% TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = { 0xC0, 0x12 } +-define(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#12)>>). + +%% TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = { 0xC0, 0x13 } +-define(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#13)>>). + +%% TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = { 0xC0, 0x14 } +-define(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#14)>>). + +%% ECDH_anon + +%% TLS_ECDH_anon_WITH_NULL_SHA = { 0xC0, 0x15 } +-define(TLS_ECDH_anon_WITH_NULL_SHA, <<?BYTE(16#C0), ?BYTE(16#15)>>). + +%% TLS_ECDH_anon_WITH_RC4_128_SHA = { 0xC0, 0x16 } +-define(TLS_ECDH_anon_WITH_RC4_128_SHA, <<?BYTE(16#C0), ?BYTE(16#16)>>). + +%% TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = { 0xC0, 0x17 } +-define(TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#17)>>). + +%% TLS_ECDH_anon_WITH_AES_128_CBC_SHA = { 0xC0, 0x18 } +-define(TLS_ECDH_anon_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#18)>>). + +%% TLS_ECDH_anon_WITH_AES_256_CBC_SHA = { 0xC0, 0x19 } +-define(TLS_ECDH_anon_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#19)>>). + + +%% RFC 5289 EC TLS suites + +%% TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = {0xC0,0x23}; +-define(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#C0), ?BYTE(16#23)>>). + +%% TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = {0xC0,0x24}; +-define(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, <<?BYTE(16#C0), ?BYTE(16#24)>>). + +%% TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = {0xC0,0x25}; +-define(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#C0), ?BYTE(16#25)>>). + +%% TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = {0xC0,0x26}; +-define(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, <<?BYTE(16#C0), ?BYTE(16#26)>>). + +%% TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = {0xC0,0x27}; +-define(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#C0), ?BYTE(16#27)>>). + +%% TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = {0xC0,0x28}; +-define(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, <<?BYTE(16#C0), ?BYTE(16#28)>>). + +%% TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = {0xC0,0x29}; +-define(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#C0), ?BYTE(16#29)>>). + +%% TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = {0xC0,0x2A}; +-define(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, <<?BYTE(16#C0), ?BYTE(16#2A)>>). + %%% Kerberos Cipher Suites %% TLS_KRB5_WITH_DES_CBC_SHA = { 0x00,0x1E }; @@ -250,4 +364,109 @@ %% hello extension data as they should. -define(TLS_EMPTY_RENEGOTIATION_INFO_SCSV, <<?BYTE(16#00), ?BYTE(16#FF)>>). +%%% PSK Cipher Suites RFC 4279 + +%% TLS_PSK_WITH_RC4_128_SHA = { 0x00, 0x8A }; +-define(TLS_PSK_WITH_RC4_128_SHA, <<?BYTE(16#00), ?BYTE(16#8A)>>). + +%% TLS_PSK_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x8B }; +-define(TLS_PSK_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#8B)>>). + +%% TLS_PSK_WITH_AES_128_CBC_SHA = { 0x00, 0x8C }; +-define(TLS_PSK_WITH_AES_128_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#8C)>>). + +%% TLS_PSK_WITH_AES_256_CBC_SHA = { 0x00, 0x8D }; +-define(TLS_PSK_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#8D)>>). + +%% TLS_DHE_PSK_WITH_RC4_128_SHA = { 0x00, 0x8E }; +-define(TLS_DHE_PSK_WITH_RC4_128_SHA, <<?BYTE(16#00), ?BYTE(16#8E)>>). + +%% TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x8F }; +-define(TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#8F)>>). + +%% TLS_DHE_PSK_WITH_AES_128_CBC_SHA = { 0x00, 0x90 }; +-define(TLS_DHE_PSK_WITH_AES_128_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#90)>>). + +%% TLS_DHE_PSK_WITH_AES_256_CBC_SHA = { 0x00, 0x91 }; +-define(TLS_DHE_PSK_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#91)>>). + +%% TLS_RSA_PSK_WITH_RC4_128_SHA = { 0x00, 0x92 }; +-define(TLS_RSA_PSK_WITH_RC4_128_SHA, <<?BYTE(16#00), ?BYTE(16#92)>>). + +%% TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x93 }; +-define(TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#93)>>). + +%% TLS_RSA_PSK_WITH_AES_128_CBC_SHA = { 0x00, 0x94 }; +-define(TLS_RSA_PSK_WITH_AES_128_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#94)>>). + +%% TLS_RSA_PSK_WITH_AES_256_CBC_SHA = { 0x00, 0x95 }; +-define(TLS_RSA_PSK_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#95)>>). + +%%% TLS 1.2 PSK Cipher Suites RFC 5487 + +%% TLS_PSK_WITH_AES_128_CBC_SHA256 = {0x00,0xAE}; +-define(TLS_PSK_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#AE)>>). + +%% TLS_PSK_WITH_AES_256_CBC_SHA384 = {0x00,0xAF}; +-define(TLS_PSK_WITH_AES_256_CBC_SHA384, <<?BYTE(16#00), ?BYTE(16#AF)>>). + +%% TLS_PSK_WITH_NULL_SHA256 = {0x00,0xB0}; +-define(TLS_PSK_WITH_NULL_SHA256, <<?BYTE(16#00), ?BYTE(16#B0)>>). + +%% TLS_PSK_WITH_NULL_SHA384 = {0x00,0xB1}; +-define(TLS_PSK_WITH_NULL_SHA384, <<?BYTE(16#00), ?BYTE(16#B1)>>). + +%% TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = {0x00,0xB2}; +-define(TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#B2)>>). + +%% TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = {0x00,0xB3}; +-define(TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, <<?BYTE(16#00), ?BYTE(16#B3)>>). + +%% TLS_DHE_PSK_WITH_NULL_SHA256 = {0x00,0xB4}; +-define(TLS_DHE_PSK_WITH_NULL_SHA256, <<?BYTE(16#00), ?BYTE(16#B4)>>). + +%% TLS_DHE_PSK_WITH_NULL_SHA384 = {0x00,0xB5}; +-define(TLS_DHE_PSK_WITH_NULL_SHA384, <<?BYTE(16#00), ?BYTE(16#B5)>>). + +%% TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = {0x00,0xB6}; +-define(TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#B6)>>). + +%% TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = {0x00,0xB7}; +-define(TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, <<?BYTE(16#00), ?BYTE(16#B7)>>). + +%% TLS_RSA_PSK_WITH_NULL_SHA256 = {0x00,0xB8}; +-define(TLS_RSA_PSK_WITH_NULL_SHA256, <<?BYTE(16#00), ?BYTE(16#B8)>>). + +%% TLS_RSA_PSK_WITH_NULL_SHA384 = {0x00,0xB9}; +-define(TLS_RSA_PSK_WITH_NULL_SHA384, <<?BYTE(16#00), ?BYTE(16#B9)>>). + +%%% SRP Cipher Suites RFC 5054 + +%% TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = { 0xC0,0x1A }; +-define(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#1A)>>). + +%% TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = { 0xC0,0x1B }; +-define(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#1B)>>). + +%% TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = { 0xC0,0x1C }; +-define(TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#1C)>>). + +%% TLS_SRP_SHA_WITH_AES_128_CBC_SHA = { 0xC0,0x1D }; +-define(TLS_SRP_SHA_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#1D)>>). + +%% TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = { 0xC0,0x1E }; +-define(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#1E)>>). + +%% TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = { 0xC0,0x1F }; +-define(TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#1F)>>). + +%% TLS_SRP_SHA_WITH_AES_256_CBC_SHA = { 0xC0,0x20 }; +-define(TLS_SRP_SHA_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#20)>>). + +%% TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = { 0xC0,0x21 }; +-define(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#21)>>). + +%% TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = { 0xC0,0x22 }; +-define(TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#22)>>). + -endif. % -ifdef(ssl_cipher). diff --git a/lib/ssl/src/ssl_connection_sup.erl b/lib/ssl/src/ssl_connection_sup.erl index 78cfda5e63..fb1c6e11a6 100644 --- a/lib/ssl/src/ssl_connection_sup.erl +++ b/lib/ssl/src/ssl_connection_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -56,10 +56,10 @@ init(_O) -> MaxT = 3600, Name = undefined, % As simple_one_for_one is used. - StartFunc = {ssl_connection, start_link, []}, + StartFunc = {tls_connection, start_link, []}, Restart = temporary, % E.g. should not be restarted Shutdown = 4000, - Modules = [ssl_connection], + Modules = [tls_connection], Type = worker, ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules}, diff --git a/lib/ssl/src/ssl_debug.erl b/lib/ssl/src/ssl_debug.erl deleted file mode 100644 index 625889c43b..0000000000 --- a/lib/ssl/src/ssl_debug.erl +++ /dev/null @@ -1,99 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% - -%% - -%%% Purpose : some debug utilities - --module(ssl_debug). - --export([unhex/1, hexd/1, hex_data/2, term_data/2, hex_data/4, term_data/4, make_binary/1]). - -%% external - -hex_data(Name, Data) -> - io:format("~s\n~s", [Name, hex(Data)]). - -term_data(Name, Term) -> - io:format("~s\n~p\n", [Name, Term]). - -hex_data(Name, Data, Mod, Line) -> - io:format("~w:~p ~s\n~s", [Mod, Line, Name, hex(Data)]). - -term_data(Name, Term, Mod, Line) -> - io:format("~w:~p ~s\n~p\n", [Mod, Line, Name, Term]). - -unhex(S) -> - Lines = string:tokens(S, "\n"), - H = [unhex(L, []) || L <- Lines], - list_to_binary(H). - -make_binary(Size) -> - crypto:rand_bytes(Size). - -%% internal - -is_hex_digit(C) when C >= $0, C =< $9 -> true; -is_hex_digit(C) when C >= $A, C =< $F -> true; -is_hex_digit(C) when C >= $a, C =< $f -> true; -is_hex_digit(_) -> false. - -unhex([], Acc) -> - list_to_binary(lists:reverse(Acc)); -unhex([_], Acc) -> - unhex([], Acc); -unhex([$ | Tl], Acc) -> - unhex(Tl, Acc); -unhex([D1, D2 | Tl], Acc) -> - case {is_hex_digit(D1), is_hex_digit(D2)} of - {true, true} -> - unhex(Tl, [erlang:list_to_integer([D1, D2], 16) | Acc]); - _ -> - unhex([], Acc) - end. - -hexd(B) -> - io:format("~s\n", [hex(B)]). - -hex(B) -> hex(erlang:iolist_to_binary(B), []). - -hex_asc(B) -> - L = binary_to_list(B), - {hexify(L), asciify(L)}. - -hex(<<B:16/binary, Rest/binary>>, Acc) -> - {HS, AS} = hex_asc(B), - hex(Rest, ["\n", AS, " ", HS | Acc]); -hex(<<>>, Acc) -> - lists:reverse(Acc); -hex(B, Acc) -> - {HS, AS} = hex_asc(B), - L = erlang:iolist_size(HS), - lists:flatten(lists:reverse(Acc, [HS, lists:duplicate(3*16 - L, $ ), " ", AS, "\n"])). - -hexify(L) -> [[hex_byte(B), " "] || B <- L]. - -hex_byte(B) when B < 16#10 -> ["0", erlang:integer_to_list(B, 16)]; -hex_byte(B) -> erlang:integer_to_list(B, 16). - -asciify(L) -> [ascii_byte(C) || C <- L]. - -ascii_byte($") -> $.; -ascii_byte(C) when C < 32; C >= 127 -> $.; -ascii_byte(C) -> C. diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index cc17dc2975..eb1a1dbf62 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -20,7 +20,7 @@ %% %%---------------------------------------------------------------------- %% Purpose: Record and constant defenitions for the SSL-handshake protocol -%% see RFC 4346 +%% see RFC 5246. Also includes supported hello extensions. %%---------------------------------------------------------------------- -ifndef(ssl_handshake). @@ -28,11 +28,13 @@ -include_lib("public_key/include/public_key.hrl"). --type algo_oid() :: ?'rsaEncryption' | ?'id-dsa'. --type public_key_params() :: #'Dss-Parms'{} | term(). --type public_key_info() :: {algo_oid(), #'RSAPublicKey'{} | integer() , public_key_params()}. +-type oid() :: tuple(). +-type public_key_params() :: #'Dss-Parms'{} | {namedCurve, oid()} | #'ECParameters'{} | term(). +-type public_key_info() :: {oid(), #'RSAPublicKey'{} | integer() | #'ECPoint'{}, public_key_params()}. -type tls_handshake_history() :: {[binary()], [binary()]}. +-define(NO_PROTOCOL, <<>>). + %% Signature algorithms -define(ANON, 0). -define(RSA, 1). @@ -46,6 +48,7 @@ compression_method, cipher_suite, master_secret, + srp_username, is_resumable, time_stamp }). @@ -88,17 +91,10 @@ % -define(NULL, 0). %% Already defined by ssl_internal.hrl %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Hello messages - RFC 4346 section 7.4.2 +%%% Hello messages - RFC 5246 section 7.4.1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --record(client_hello, { - client_version, - random, - session_id, % opaque SessionID<0..32> - cipher_suites, % cipher_suites<2..2^16-1> - compression_methods, % compression_methods<1..2^8-1>, - renegotiation_info, - hash_signs % supported combinations of hashes/signature algos - }). + +%% client_hello defined in tls_handshake.hrl and dtls_handshake.hrl -record(server_hello, { server_version, @@ -107,11 +103,14 @@ cipher_suite, % cipher_suites compression_method, % compression_method renegotiation_info, - hash_signs % supported combinations of hashes/signature algos + hash_signs, % supported combinations of hashes/signature algos + ec_point_formats, % supported ec point formats + elliptic_curves, % supported elliptic curver + next_protocol_negotiation = undefined % [binary()] }). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Server authentication and key exchange messages - RFC 4346 section 7.4.3 +%%% Server authentication and key exchange messages - RFC 5246 section 7.4.3 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% opaque ASN.1Cert<2^24-1>; @@ -124,6 +123,11 @@ -define(KEY_EXCHANGE_RSA, 0). -define(KEY_EXCHANGE_DIFFIE_HELLMAN, 1). +-define(KEY_EXCHANGE_EC_DIFFIE_HELLMAN, 6). +-define(KEY_EXCHANGE_PSK, 2). +-define(KEY_EXCHANGE_DHE_PSK, 3). +-define(KEY_EXCHANGE_RSA_PSK, 4). +-define(KEY_EXCHANGE_SRP, 5). -record(server_rsa_params, { rsa_modulus, %% opaque RSA_modulus<1..2^16-1> @@ -135,11 +139,37 @@ dh_g, %% opaque DH_g<1..2^16-1> dh_y %% opaque DH_Ys<1..2^16-1> }). - + +-record(server_ecdh_params, { + curve, + public %% opaque encoded ECpoint + }). + +-record(server_psk_params, { + hint + }). + +-record(server_dhe_psk_params, { + hint, + dh_params + }). + +-record(server_srp_params, { + srp_n, %% opaque srp_N<1..2^16-1> + srp_g, %% opaque srp_g<1..2^16-1> + srp_s, %% opaque srp_s<1..2^8-1> + srp_b %% opaque srp_B<1..2^16-1> + }). + -record(server_key_exchange, { + exchange_keys + }). + +-record(server_key_params, { params, %% #server_rsa_params{} | #server_dh_params{} - signed_params, %% #signature{} - hashsign %% term(atom(), atom()) + params_bin, + hashsign, %% term(atom(), atom()) + signature %% #signature{} }). %% enum { anonymous, rsa, dsa } SignatureAlgorithm; @@ -152,7 +182,7 @@ -record(server_hello_done, {}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Certificate request - RFC 4346 section 7.4.4 +%%% Certificate request - RFC 5246 section 7.4.4 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% enum { @@ -164,6 +194,9 @@ -define(DSS_SIGN, 2). -define(RSA_FIXED_DH, 3). -define(DSS_FIXED_DH, 4). +-define(ECDSA_SIGN, 64). +-define(RSA_FIXED_ECDH, 65). +-define(ECDSA_FIXED_ECDH, 66). % opaque DistinguishedName<1..2^16-1>; @@ -200,6 +233,28 @@ dh_public }). +-record(client_ec_diffie_hellman_public, { + dh_public + }). + +-record(client_psk_identity, { + identity + }). + +-record(client_dhe_psk_identity, { + identity, + dh_public + }). + +-record(client_rsa_psk_identity, { + identity, + exchange_keys + }). + +-record(client_srp_public, { + srp_a + }). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Certificate verify - RFC 4346 section 7.4.8 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -226,6 +281,15 @@ }). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% SRP RFC 5054 section 2.8.1. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-define(SRP_EXT, 12). + +-record(srp, { + username + }). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Signature Algorithms RFC 5746 section 7.4.1.4.1. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -define(SIGNATURE_ALGORITHMS_EXT, 13). @@ -234,6 +298,45 @@ hash_sign_algos }). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Next Protocol Negotiation +%% (http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-02) +%% (http://technotes.googlecode.com/git/nextprotoneg.html) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(NEXTPROTONEG_EXT, 13172). +-define(NEXT_PROTOCOL, 67). +-record(next_protocol_negotiation, {extension_data}). + +-record(next_protocol, {selected_protocol}). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% ECC Extensions RFC 4492 section 4 and 5 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(ELLIPTIC_CURVES_EXT, 10). +-define(EC_POINT_FORMATS_EXT, 11). + +-record(elliptic_curves, { + elliptic_curve_list + }). + +-record(ec_point_formats, { + ec_point_format_list + }). + +-define(ECPOINT_UNCOMPRESSED, 0). +-define(ECPOINT_ANSIX962_COMPRESSED_PRIME, 1). +-define(ECPOINT_ANSIX962_COMPRESSED_CHAR2, 2). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% ECC RFC 4492 Handshake Messages, Section 5 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(EXPLICIT_PRIME, 1). +-define(EXPLICIT_CHAR2, 2). +-define(NAMED_CURVE, 3). + -endif. % -ifdef(ssl_handshake). diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index d8815369e9..de8d20d399 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -37,9 +37,9 @@ -type tls_atom_version() :: sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'. -type certdb_ref() :: reference(). -type db_handle() :: term(). --type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon. +-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon. -type der_cert() :: binary(). --type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{}. +-type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{} | #'ECPrivateKey'{}. -type issuer() :: tuple(). -type serialnumber() :: integer(). -type cert_key() :: {reference(), integer(), issuer()}. @@ -69,8 +69,8 @@ -define(TRUE, 0). -define(FALSE, 1). --define(DEFAULT_SUPPORTED_VERSIONS, [tlsv1, sslv3]). %% Add 'tlsv1.1' in R16 -define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]). +-define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1, sslv3]). -record(ssl_options, { versions, % 'tlsv1.2' | 'tlsv1.1' | tlsv1 | sslv3 @@ -90,6 +90,9 @@ cacertfile, % file() dh, % der_encoded() dhfile, % file() + user_lookup_fun, % server option, fun to lookup the user + psk_identity, % binary + srp_identity, % client option {User, Password} ciphers, % %% Local policy for the server if it want's to reuse the session %% or not. Defaluts to allways returning true. @@ -107,6 +110,8 @@ % go into hibernation %% This option should only be set to true by inet_tls_dist erl_dist = false, + next_protocols_advertised = undefined, %% [binary()], + next_protocol_selector = undefined, %% fun([binary()]) -> binary()) log_alert }). diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 0cf4f2ce33..7af4a68461 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -24,8 +24,6 @@ -module(ssl_manager). -behaviour(gen_server). --include("ssl_internal.hrl"). - %% Internal application API -export([start_link/1, start_link_dist/1, connection_init/2, cache_pem_file/2, @@ -105,8 +103,8 @@ connection_init(Trustedcerts, Role) -> %% Description: Cach a pem file and return its content. %%-------------------------------------------------------------------- cache_pem_file(File, DbHandle) -> - MD5 = crypto:md5(File), - case ssl_certificate_db:lookup_cached_pem(DbHandle, MD5) of + MD5 = crypto:hash(md5, File), + case ssl_pkix_db:lookup_cached_pem(DbHandle, MD5) of [{Content,_}] -> {ok, Content}; [Content] -> @@ -134,7 +132,7 @@ clear_pem_cache() -> %% serialnumber(), issuer()}. %% -------------------------------------------------------------------- lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> - ssl_certificate_db:lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer). + ssl_pkix_db:lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer). %%-------------------------------------------------------------------- -spec new_session_id(integer()) -> session_id(). @@ -144,8 +142,15 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> new_session_id(Port) -> call({new_session_id, Port}). +%%-------------------------------------------------------------------- +-spec clean_cert_db(reference(), binary()) -> ok. +%% +%% Description: Send clean request of cert db to ssl_manager process should +%% be called by ssl-connection processes. +%%-------------------------------------------------------------------- clean_cert_db(Ref, File) -> - erlang:send_after(?CLEAN_CERT_DB, self(), {clean_cert_db, Ref, File}). + erlang:send_after(?CLEAN_CERT_DB, get(ssl_manager), {clean_cert_db, Ref, File}), + ok. %%-------------------------------------------------------------------- -spec register_session(inet:port_number(), #session{}) -> ok. @@ -189,7 +194,7 @@ init([Name, Opts]) -> CacheCb = proplists:get_value(session_cb, Opts, ssl_session_cache), SessionLifeTime = proplists:get_value(session_lifetime, Opts, ?'24H_in_sec'), - CertDb = ssl_certificate_db:create(), + CertDb = ssl_pkix_db:create(), SessionCache = CacheCb:init(proplists:get_value(session_cb_init_args, Opts, [])), Timer = erlang:send_after(SessionLifeTime * 1000 + 5000, self(), validate_sessions), @@ -222,7 +227,7 @@ handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From, session_cache = Cache} = State) -> Result = try - {ok, Ref} = ssl_certificate_db:add_trusted_certs(Pid, Trustedcerts, Db), + {ok, Ref} = ssl_pkix_db:add_trusted_certs(Pid, Trustedcerts, Db), {ok, Ref, CertDb, FileRefDb, PemChace, Cache} catch _:Reason -> @@ -239,7 +244,7 @@ handle_call({{new_session_id,Port}, _}, handle_call({{cache_pem, File}, _Pid}, _, #state{certificate_db = Db} = State) -> - try ssl_certificate_db:cache_pem_file(File, Db) of + try ssl_pkix_db:cache_pem_file(File, Db) of Result -> {reply, Result, State} catch @@ -247,7 +252,7 @@ handle_call({{cache_pem, File}, _Pid}, _, {reply, {error, Reason}, State} end; handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_,PemChace]} = State) -> - ssl_certificate_db:clear(PemChace), + ssl_pkix_db:clear(PemChace), {reply, ok, State}. %%-------------------------------------------------------------------- @@ -310,11 +315,11 @@ handle_info({delayed_clean_session, Key}, #state{session_cache = Cache, {noreply, State}; handle_info(clear_pem_cache, #state{certificate_db = [_,_,PemChace]} = State) -> - case ssl_certificate_db:db_size(PemChace) of + case ssl_pkix_db:db_size(PemChace) of N when N < ?NOT_TO_BIG -> ok; _ -> - ssl_certificate_db:clear(PemChace) + ssl_pkix_db:clear(PemChace) end, erlang:send_after(?CLEAR_PEM_CACHE, self(), clear_pem_cache), {noreply, State}; @@ -322,19 +327,12 @@ handle_info(clear_pem_cache, #state{certificate_db = [_,_,PemChace]} = State) -> handle_info({clean_cert_db, Ref, File}, #state{certificate_db = [CertDb,RefDb, PemCache]} = State) -> - case ssl_certificate_db:ref_count(Ref, RefDb, 0) of - 0 -> - MD5 = crypto:md5(File), - case ssl_certificate_db:lookup_cached_pem(PemCache, MD5) of - [{Content, Ref}] -> - ssl_certificate_db:insert(MD5, Content, PemCache); - undefined -> - ok - end, - ssl_certificate_db:remove(Ref, RefDb), - ssl_certificate_db:remove_trusted_certs(Ref, CertDb); + + case ssl_pkix_db:lookup(Ref, RefDb) of + undefined -> %% Alredy cleaned + ok; _ -> - ok + clean_cert_db(Ref, CertDb, RefDb, PemCache, File) end, {noreply, State}; @@ -347,7 +345,7 @@ handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- --spec terminate(reason(), #state{}) -> term(). +-spec terminate(reason(), #state{}) -> ok. %% %% Description: This function is called by a gen_server when it is about to %% terminate. It should be the opposite of Module:init/1 and do any necessary @@ -359,7 +357,7 @@ terminate(_Reason, #state{certificate_db = Db, session_cache_cb = CacheCb, session_validation_timer = Timer}) -> erlang:cancel_timer(Timer), - ssl_certificate_db:remove(Db), + ssl_pkix_db:remove(Db), CacheCb:terminate(SessionCache), ok. @@ -466,3 +464,19 @@ new_id(Port, Tries, Cache, CacheCb) -> _ -> new_id(Port, Tries - 1, Cache, CacheCb) end. + +clean_cert_db(Ref, CertDb, RefDb, PemCache, File) -> + case ssl_pkix_db:ref_count(Ref, RefDb, 0) of + 0 -> + MD5 = crypto:hash(md5, File), + case ssl_pkix_db:lookup_cached_pem(PemCache, MD5) of + [{Content, Ref}] -> + ssl_pkix_db:insert(MD5, Content, PemCache); + _ -> + ok + end, + ssl_pkix_db:remove(Ref, RefDb), + ssl_pkix_db:remove_trusted_certs(Ref, CertDb); + _ -> + ok + end. diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_pkix_db.erl index 67d00f0da7..9de50c8f26 100644 --- a/lib/ssl/src/ssl_certificate_db.erl +++ b/lib/ssl/src/ssl_pkix_db.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -18,10 +18,11 @@ %% %%---------------------------------------------------------------------- -%% Purpose: Storage for trused certificats +%% Purpose: Storage for trusted certificates %%---------------------------------------------------------------------- --module(ssl_certificate_db). +-module(ssl_pkix_db). + -include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). -include_lib("kernel/include/file.hrl"). @@ -37,7 +38,7 @@ %%==================================================================== %%-------------------------------------------------------------------- --spec create() -> [db_handle()]. +-spec create() -> [db_handle(),...]. %% %% Description: Creates a new certificate db. %% Note: lookup_trusted_cert/4 may be called from any process but only @@ -54,7 +55,7 @@ create() -> ]. %%-------------------------------------------------------------------- --spec remove([db_handle()]) -> term(). +-spec remove([db_handle()]) -> ok. %% %% Description: Removes database db %%-------------------------------------------------------------------- @@ -99,7 +100,7 @@ add_trusted_certs(_Pid, {der, DerList}, [CerDb, _,_]) -> {ok, NewRef}; add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) -> - MD5 = crypto:md5(File), + MD5 = crypto:hash(md5, File), case lookup_cached_pem(Db, MD5) of [{_Content, Ref}] -> ref_count(Ref, RefDb, 1), @@ -114,8 +115,8 @@ add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) -> new_trusted_cert_entry({MD5, File}, Db) end. %%-------------------------------------------------------------------- --spec cache_pem_file({binary(), binary()}, [db_handle()]) -> term(). --spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> term(). +-spec cache_pem_file({binary(), binary()}, [db_handle()]) -> {ok, term()}. +-spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> {ok, term()}. %% %% Description: Cache file as binary in DB %%-------------------------------------------------------------------- @@ -131,19 +132,25 @@ cache_pem_file(Ref, {MD5, File}, [_CertsDb, _RefDb, PemChache]) -> insert(MD5, {Content, Ref}, PemChache), {ok, Content}. +%%-------------------------------------------------------------------- +-spec remove_trusted_certs(reference(), db_handle()) -> ok. +%% +%% Description: Removes all trusted certificates refernced by <Ref>. +%%-------------------------------------------------------------------- remove_trusted_certs(Ref, CertsDb) -> remove_certs(Ref, CertsDb). %%-------------------------------------------------------------------- --spec remove(term(), db_handle()) -> term(). +-spec remove(term(), db_handle()) -> ok. %% %% Description: Removes an element in a <Db>. %%-------------------------------------------------------------------- remove(Key, Db) -> - _ = ets:delete(Db, Key). + ets:delete(Db, Key), + ok. %%-------------------------------------------------------------------- --spec lookup(term(), db_handle()) -> term() | undefined. +-spec lookup(term(), db_handle()) -> [term()] | undefined. %% %% Description: Looks up an element in a <Db>. %%-------------------------------------------------------------------- @@ -158,7 +165,7 @@ lookup(Key, Db) -> [Pick(Data) || Data <- Contents] end. %%-------------------------------------------------------------------- --spec foldl(fun(), term(), db_handle()) -> term(). +-spec foldl(fun((_,_) -> term()), term(), db_handle()) -> term(). %% %% Description: Calls Fun(Elem, AccIn) on successive elements of the %% cache, starting with AccIn == Acc0. Fun/2 must return a new @@ -178,12 +185,13 @@ ref_count(Key, Db, N) -> ets:update_counter(Db,Key,N). %%-------------------------------------------------------------------- --spec clear(db_handle()) -> term(). +-spec clear(db_handle()) -> ok. %% %% Description: Clears the cache %%-------------------------------------------------------------------- clear(Db) -> - ets:delete_all_objects(Db). + true = ets:delete_all_objects(Db), + ok. %%-------------------------------------------------------------------- -spec db_size(db_handle()) -> integer(). @@ -194,30 +202,35 @@ db_size(Db) -> ets:info(Db, size). %%-------------------------------------------------------------------- -%%-spec insert(Key::term(), Data::term(), Db::db_handle()) -> no_return(). +-spec insert(Key::term(), Data::term(), Db::db_handle()) -> ok. %% %% Description: Inserts data into <Db> %%-------------------------------------------------------------------- insert(Key, Data, Db) -> - true = ets:insert(Db, {Key, Data}). + true = ets:insert(Db, {Key, Data}), + ok. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- update_counter(Key, Count, Db) -> - true = ets:insert(Db, {Key, Count}). + true = ets:insert(Db, {Key, Count}), + ok. remove_certs(Ref, CertsDb) -> - ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}). + true = ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}), + ok. add_certs_from_der(DerList, Ref, CertsDb) -> Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end, - [Add(Cert) || Cert <- DerList]. + [Add(Cert) || Cert <- DerList], + ok. add_certs_from_pem(PemEntries, Ref, CertsDb) -> Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end, - [Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries]. - + [Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries], + ok. + add_certs(Cert, Ref, CertsDb) -> try ErlCert = public_key:pkix_decode_cert(Cert, otp), TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate, diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl index f73da92a52..2fd17f9c35 100644 --- a/lib/ssl/src/ssl_record.hrl +++ b/lib/ssl/src/ssl_record.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -143,34 +143,6 @@ -define(LOWEST_MAJOR_SUPPORTED_VERSION, 3). --record(ssl_tls, { %% From inet driver - port, - type, - version, - fragment - }). - -%% -record(tls_plain_text, { -%% type, -%% version, % #protocol_version{} -%% length, % unit 16 -%% fragment % opaque -%% }). - -%% -record(tls_compressed, { -%% type, -%% version, -%% length, % unit 16 -%% fragment % opaque -%% }). - -%% -record(tls_cipher_text, { -%% type, -%% version, -%% length, -%% cipher, -%% fragment -%% }). -record(generic_stream_cipher, { content, % opaque content[TLSCompressed.length]; diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index 2ad422fc03..a24b2d9444 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -72,15 +72,12 @@ valid_session(#session{time_stamp = TimeStamp}, LifeTime) -> server_id(Port, <<>>, _SslOpts, _Cert, _, _) -> {ssl_manager:new_session_id(Port), undefined}; -server_id(Port, SuggestedId, - #ssl_options{reuse_sessions = ReuseEnabled, - reuse_session = ReuseFun}, - Cert, Cache, CacheCb) -> +server_id(Port, SuggestedId, Options, Cert, Cache, CacheCb) -> LifeTime = case application:get_env(ssl, session_lifetime) of {ok, Time} when is_integer(Time) -> Time; _ -> ?'24H_in_sec' end, - case is_resumable(SuggestedId, Port, ReuseEnabled,ReuseFun, + case is_resumable(SuggestedId, Port, Options, Cache, CacheCb, LifeTime, Cert) of {true, Resumed} -> @@ -112,9 +109,9 @@ select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) -> [[Id, _]|_] -> Id end. -is_resumable(_, _, false, _, _, _, _, _) -> +is_resumable(_, _, #ssl_options{reuse_sessions = false}, _, _, _, _) -> {false, undefined}; -is_resumable(SuggestedSessionId, Port, true, ReuseFun, Cache, +is_resumable(SuggestedSessionId, Port, #ssl_options{reuse_session = ReuseFun} = Options, Cache, CacheCb, SecondLifeTime, OwnCert) -> case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of #session{cipher_suite = CipherSuite, @@ -125,6 +122,7 @@ is_resumable(SuggestedSessionId, Port, true, ReuseFun, Cache, case resumable(IsResumable) andalso (OwnCert == SessionOwnCert) andalso valid_session(Session, SecondLifeTime) + andalso reusable_options(Options, Session) andalso ReuseFun(SuggestedSessionId, PeerCert, Compression, CipherSuite) of @@ -139,3 +137,9 @@ resumable(new) -> false; resumable(IsResumable) -> IsResumable. + +reusable_options(#ssl_options{fail_if_no_peer_cert = true, + verify = verify_peer}, Session) -> + (Session#session.peer_certificate =/= undefined); +reusable_options(_,_) -> + true. diff --git a/lib/ssl/src/ssl_socket.erl b/lib/ssl/src/ssl_socket.erl new file mode 100644 index 0000000000..4778db2333 --- /dev/null +++ b/lib/ssl/src/ssl_socket.erl @@ -0,0 +1,35 @@ +-module(ssl_socket). + +-include("ssl_internal.hrl"). + +-export([socket/3, setopts/3, getopts/3, peername/2, sockname/2, port/2]). + +socket(Pid, Transport, Socket) -> + #sslsocket{pid = Pid, + %% "The name "fd" is keept for backwards compatibility + fd = {Transport, Socket}}. + +setopts(gen_tcp, Socket, Options) -> + inet:setopts(Socket, Options); +setopts(Transport, Socket, Options) -> + Transport:setopts(Socket, Options). + +getopts(gen_tcp, Socket, Options) -> + inet:getopts(Socket, Options); +getopts(Transport, Socket, Options) -> + Transport:getopts(Socket, Options). + +peername(gen_tcp, Socket) -> + inet:peername(Socket); +peername(Transport, Socket) -> + Transport:peername(Socket). + +sockname(gen_tcp, Socket) -> + inet:sockname(Socket); +sockname(Transport, Socket) -> + Transport:sockname(Socket). + +port(gen_tcp, Socket) -> + inet:port(Socket); +port(Transport, Socket) -> + Transport:port(Socket). diff --git a/lib/ssl/src/ssl_srp.hrl b/lib/ssl/src/ssl_srp.hrl new file mode 100644 index 0000000000..ab2be33ab2 --- /dev/null +++ b/lib/ssl/src/ssl_srp.hrl @@ -0,0 +1,31 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Record definition for the TLS SRP protocol +%% see RFC 5054 +%%---------------------------------------------------------------------- + +-record(srp_user, { + generator :: binary(), + prime :: binary(), + salt :: binary(), + verifier :: binary() + }). diff --git a/lib/ssl/src/ssl_srp_primes.erl b/lib/ssl/src/ssl_srp_primes.erl new file mode 100644 index 0000000000..ca20a8d673 --- /dev/null +++ b/lib/ssl/src/ssl_srp_primes.erl @@ -0,0 +1,506 @@ +-module(ssl_srp_primes). + +-export([get_srp_params/1, check_srp_params/2]). + +-define(PRIME_1024, <<16#EE, 16#AF, 16#0A, 16#B9, 16#AD, 16#B3, 16#8D, + 16#D6, 16#9C, 16#33, 16#F8, 16#0A, 16#FA, 16#8F, + 16#C5, 16#E8, 16#60, 16#72, 16#61, 16#87, 16#75, + 16#FF, 16#3C, 16#0B, 16#9E, 16#A2, 16#31, 16#4C, + 16#9C, 16#25, 16#65, 16#76, 16#D6, 16#74, 16#DF, + 16#74, 16#96, 16#EA, 16#81, 16#D3, 16#38, 16#3B, + 16#48, 16#13, 16#D6, 16#92, 16#C6, 16#E0, 16#E0, + 16#D5, 16#D8, 16#E2, 16#50, 16#B9, 16#8B, 16#E4, + 16#8E, 16#49, 16#5C, 16#1D, 16#60, 16#89, 16#DA, + 16#D1, 16#5D, 16#C7, 16#D7, 16#B4, 16#61, 16#54, + 16#D6, 16#B6, 16#CE, 16#8E, 16#F4, 16#AD, 16#69, + 16#B1, 16#5D, 16#49, 16#82, 16#55, 16#9B, 16#29, + 16#7B, 16#CF, 16#18, 16#85, 16#C5, 16#29, 16#F5, + 16#66, 16#66, 16#0E, 16#57, 16#EC, 16#68, 16#ED, + 16#BC, 16#3C, 16#05, 16#72, 16#6C, 16#C0, 16#2F, + 16#D4, 16#CB, 16#F4, 16#97, 16#6E, 16#AA, 16#9A, + 16#FD, 16#51, 16#38, 16#FE, 16#83, 16#76, 16#43, + 16#5B, 16#9F, 16#C6, 16#1D, 16#2F, 16#C0, 16#EB, + 16#06, 16#E3>>). +-define(GENERATOR_1024, <<2>>). + + +-define(PRIME_1536, <<16#9D, 16#EF, 16#3C, 16#AF, 16#B9, 16#39, 16#27, + 16#7A, 16#B1, 16#F1, 16#2A, 16#86, 16#17, 16#A4, + 16#7B, 16#BB, 16#DB, 16#A5, 16#1D, 16#F4, 16#99, + 16#AC, 16#4C, 16#80, 16#BE, 16#EE, 16#A9, 16#61, + 16#4B, 16#19, 16#CC, 16#4D, 16#5F, 16#4F, 16#5F, + 16#55, 16#6E, 16#27, 16#CB, 16#DE, 16#51, 16#C6, + 16#A9, 16#4B, 16#E4, 16#60, 16#7A, 16#29, 16#15, + 16#58, 16#90, 16#3B, 16#A0, 16#D0, 16#F8, 16#43, + 16#80, 16#B6, 16#55, 16#BB, 16#9A, 16#22, 16#E8, + 16#DC, 16#DF, 16#02, 16#8A, 16#7C, 16#EC, 16#67, + 16#F0, 16#D0, 16#81, 16#34, 16#B1, 16#C8, 16#B9, + 16#79, 16#89, 16#14, 16#9B, 16#60, 16#9E, 16#0B, + 16#E3, 16#BA, 16#B6, 16#3D, 16#47, 16#54, 16#83, + 16#81, 16#DB, 16#C5, 16#B1, 16#FC, 16#76, 16#4E, + 16#3F, 16#4B, 16#53, 16#DD, 16#9D, 16#A1, 16#15, + 16#8B, 16#FD, 16#3E, 16#2B, 16#9C, 16#8C, 16#F5, + 16#6E, 16#DF, 16#01, 16#95, 16#39, 16#34, 16#96, + 16#27, 16#DB, 16#2F, 16#D5, 16#3D, 16#24, 16#B7, + 16#C4, 16#86, 16#65, 16#77, 16#2E, 16#43, 16#7D, + 16#6C, 16#7F, 16#8C, 16#E4, 16#42, 16#73, 16#4A, + 16#F7, 16#CC, 16#B7, 16#AE, 16#83, 16#7C, 16#26, + 16#4A, 16#E3, 16#A9, 16#BE, 16#B8, 16#7F, 16#8A, + 16#2F, 16#E9, 16#B8, 16#B5, 16#29, 16#2E, 16#5A, + 16#02, 16#1F, 16#FF, 16#5E, 16#91, 16#47, 16#9E, + 16#8C, 16#E7, 16#A2, 16#8C, 16#24, 16#42, 16#C6, + 16#F3, 16#15, 16#18, 16#0F, 16#93, 16#49, 16#9A, + 16#23, 16#4D, 16#CF, 16#76, 16#E3, 16#FE, 16#D1, + 16#35, 16#F9, 16#BB>>). +-define(GENERATOR_1536, <<2>>). + +-define(PRIME_2048, <<16#AC, 16#6B, 16#DB, 16#41, 16#32, 16#4A, 16#9A, + 16#9B, 16#F1, 16#66, 16#DE, 16#5E, 16#13, 16#89, + 16#58, 16#2F, 16#AF, 16#72, 16#B6, 16#65, 16#19, + 16#87, 16#EE, 16#07, 16#FC, 16#31, 16#92, 16#94, + 16#3D, 16#B5, 16#60, 16#50, 16#A3, 16#73, 16#29, + 16#CB, 16#B4, 16#A0, 16#99, 16#ED, 16#81, 16#93, + 16#E0, 16#75, 16#77, 16#67, 16#A1, 16#3D, 16#D5, + 16#23, 16#12, 16#AB, 16#4B, 16#03, 16#31, 16#0D, + 16#CD, 16#7F, 16#48, 16#A9, 16#DA, 16#04, 16#FD, + 16#50, 16#E8, 16#08, 16#39, 16#69, 16#ED, 16#B7, + 16#67, 16#B0, 16#CF, 16#60, 16#95, 16#17, 16#9A, + 16#16, 16#3A, 16#B3, 16#66, 16#1A, 16#05, 16#FB, + 16#D5, 16#FA, 16#AA, 16#E8, 16#29, 16#18, 16#A9, + 16#96, 16#2F, 16#0B, 16#93, 16#B8, 16#55, 16#F9, + 16#79, 16#93, 16#EC, 16#97, 16#5E, 16#EA, 16#A8, + 16#0D, 16#74, 16#0A, 16#DB, 16#F4, 16#FF, 16#74, + 16#73, 16#59, 16#D0, 16#41, 16#D5, 16#C3, 16#3E, + 16#A7, 16#1D, 16#28, 16#1E, 16#44, 16#6B, 16#14, + 16#77, 16#3B, 16#CA, 16#97, 16#B4, 16#3A, 16#23, + 16#FB, 16#80, 16#16, 16#76, 16#BD, 16#20, 16#7A, + 16#43, 16#6C, 16#64, 16#81, 16#F1, 16#D2, 16#B9, + 16#07, 16#87, 16#17, 16#46, 16#1A, 16#5B, 16#9D, + 16#32, 16#E6, 16#88, 16#F8, 16#77, 16#48, 16#54, + 16#45, 16#23, 16#B5, 16#24, 16#B0, 16#D5, 16#7D, + 16#5E, 16#A7, 16#7A, 16#27, 16#75, 16#D2, 16#EC, + 16#FA, 16#03, 16#2C, 16#FB, 16#DB, 16#F5, 16#2F, + 16#B3, 16#78, 16#61, 16#60, 16#27, 16#90, 16#04, + 16#E5, 16#7A, 16#E6, 16#AF, 16#87, 16#4E, 16#73, + 16#03, 16#CE, 16#53, 16#29, 16#9C, 16#CC, 16#04, + 16#1C, 16#7B, 16#C3, 16#08, 16#D8, 16#2A, 16#56, + 16#98, 16#F3, 16#A8, 16#D0, 16#C3, 16#82, 16#71, + 16#AE, 16#35, 16#F8, 16#E9, 16#DB, 16#FB, 16#B6, + 16#94, 16#B5, 16#C8, 16#03, 16#D8, 16#9F, 16#7A, + 16#E4, 16#35, 16#DE, 16#23, 16#6D, 16#52, 16#5F, + 16#54, 16#75, 16#9B, 16#65, 16#E3, 16#72, 16#FC, + 16#D6, 16#8E, 16#F2, 16#0F, 16#A7, 16#11, 16#1F, + 16#9E, 16#4A, 16#FF, 16#73>>). +-define(GENERATOR_2048, <<2>>). + +-define(PRIME_3072, <<16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, + 16#FF, 16#C9, 16#0F, 16#DA, 16#A2, 16#21, 16#68, + 16#C2, 16#34, 16#C4, 16#C6, 16#62, 16#8B, 16#80, + 16#DC, 16#1C, 16#D1, 16#29, 16#02, 16#4E, 16#08, + 16#8A, 16#67, 16#CC, 16#74, 16#02, 16#0B, 16#BE, + 16#A6, 16#3B, 16#13, 16#9B, 16#22, 16#51, 16#4A, + 16#08, 16#79, 16#8E, 16#34, 16#04, 16#DD, 16#EF, + 16#95, 16#19, 16#B3, 16#CD, 16#3A, 16#43, 16#1B, + 16#30, 16#2B, 16#0A, 16#6D, 16#F2, 16#5F, 16#14, + 16#37, 16#4F, 16#E1, 16#35, 16#6D, 16#6D, 16#51, + 16#C2, 16#45, 16#E4, 16#85, 16#B5, 16#76, 16#62, + 16#5E, 16#7E, 16#C6, 16#F4, 16#4C, 16#42, 16#E9, + 16#A6, 16#37, 16#ED, 16#6B, 16#0B, 16#FF, 16#5C, + 16#B6, 16#F4, 16#06, 16#B7, 16#ED, 16#EE, 16#38, + 16#6B, 16#FB, 16#5A, 16#89, 16#9F, 16#A5, 16#AE, + 16#9F, 16#24, 16#11, 16#7C, 16#4B, 16#1F, 16#E6, + 16#49, 16#28, 16#66, 16#51, 16#EC, 16#E4, 16#5B, + 16#3D, 16#C2, 16#00, 16#7C, 16#B8, 16#A1, 16#63, + 16#BF, 16#05, 16#98, 16#DA, 16#48, 16#36, 16#1C, + 16#55, 16#D3, 16#9A, 16#69, 16#16, 16#3F, 16#A8, + 16#FD, 16#24, 16#CF, 16#5F, 16#83, 16#65, 16#5D, + 16#23, 16#DC, 16#A3, 16#AD, 16#96, 16#1C, 16#62, + 16#F3, 16#56, 16#20, 16#85, 16#52, 16#BB, 16#9E, + 16#D5, 16#29, 16#07, 16#70, 16#96, 16#96, 16#6D, + 16#67, 16#0C, 16#35, 16#4E, 16#4A, 16#BC, 16#98, + 16#04, 16#F1, 16#74, 16#6C, 16#08, 16#CA, 16#18, + 16#21, 16#7C, 16#32, 16#90, 16#5E, 16#46, 16#2E, + 16#36, 16#CE, 16#3B, 16#E3, 16#9E, 16#77, 16#2C, + 16#18, 16#0E, 16#86, 16#03, 16#9B, 16#27, 16#83, + 16#A2, 16#EC, 16#07, 16#A2, 16#8F, 16#B5, 16#C5, + 16#5D, 16#F0, 16#6F, 16#4C, 16#52, 16#C9, 16#DE, + 16#2B, 16#CB, 16#F6, 16#95, 16#58, 16#17, 16#18, + 16#39, 16#95, 16#49, 16#7C, 16#EA, 16#95, 16#6A, + 16#E5, 16#15, 16#D2, 16#26, 16#18, 16#98, 16#FA, + 16#05, 16#10, 16#15, 16#72, 16#8E, 16#5A, 16#8A, + 16#AA, 16#C4, 16#2D, 16#AD, 16#33, 16#17, 16#0D, + 16#04, 16#50, 16#7A, 16#33, 16#A8, 16#55, 16#21, + 16#AB, 16#DF, 16#1C, 16#BA, 16#64, 16#EC, 16#FB, + 16#85, 16#04, 16#58, 16#DB, 16#EF, 16#0A, 16#8A, + 16#EA, 16#71, 16#57, 16#5D, 16#06, 16#0C, 16#7D, + 16#B3, 16#97, 16#0F, 16#85, 16#A6, 16#E1, 16#E4, + 16#C7, 16#AB, 16#F5, 16#AE, 16#8C, 16#DB, 16#09, + 16#33, 16#D7, 16#1E, 16#8C, 16#94, 16#E0, 16#4A, + 16#25, 16#61, 16#9D, 16#CE, 16#E3, 16#D2, 16#26, + 16#1A, 16#D2, 16#EE, 16#6B, 16#F1, 16#2F, 16#FA, + 16#06, 16#D9, 16#8A, 16#08, 16#64, 16#D8, 16#76, + 16#02, 16#73, 16#3E, 16#C8, 16#6A, 16#64, 16#52, + 16#1F, 16#2B, 16#18, 16#17, 16#7B, 16#20, 16#0C, + 16#BB, 16#E1, 16#17, 16#57, 16#7A, 16#61, 16#5D, + 16#6C, 16#77, 16#09, 16#88, 16#C0, 16#BA, 16#D9, + 16#46, 16#E2, 16#08, 16#E2, 16#4F, 16#A0, 16#74, + 16#E5, 16#AB, 16#31, 16#43, 16#DB, 16#5B, 16#FC, + 16#E0, 16#FD, 16#10, 16#8E, 16#4B, 16#82, 16#D1, + 16#20, 16#A9, 16#3A, 16#D2, 16#CA, 16#FF, 16#FF, + 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF>>). +-define(GENERATOR_3072, <<5>>). + +-define(PRIME_4096, <<16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, + 16#FF, 16#C9, 16#0F, 16#DA, 16#A2, 16#21, 16#68, + 16#C2, 16#34, 16#C4, 16#C6, 16#62, 16#8B, 16#80, + 16#DC, 16#1C, 16#D1, 16#29, 16#02, 16#4E, 16#08, + 16#8A, 16#67, 16#CC, 16#74, 16#02, 16#0B, 16#BE, + 16#A6, 16#3B, 16#13, 16#9B, 16#22, 16#51, 16#4A, + 16#08, 16#79, 16#8E, 16#34, 16#04, 16#DD, 16#EF, + 16#95, 16#19, 16#B3, 16#CD, 16#3A, 16#43, 16#1B, + 16#30, 16#2B, 16#0A, 16#6D, 16#F2, 16#5F, 16#14, + 16#37, 16#4F, 16#E1, 16#35, 16#6D, 16#6D, 16#51, + 16#C2, 16#45, 16#E4, 16#85, 16#B5, 16#76, 16#62, + 16#5E, 16#7E, 16#C6, 16#F4, 16#4C, 16#42, 16#E9, + 16#A6, 16#37, 16#ED, 16#6B, 16#0B, 16#FF, 16#5C, + 16#B6, 16#F4, 16#06, 16#B7, 16#ED, 16#EE, 16#38, + 16#6B, 16#FB, 16#5A, 16#89, 16#9F, 16#A5, 16#AE, + 16#9F, 16#24, 16#11, 16#7C, 16#4B, 16#1F, 16#E6, + 16#49, 16#28, 16#66, 16#51, 16#EC, 16#E4, 16#5B, + 16#3D, 16#C2, 16#00, 16#7C, 16#B8, 16#A1, 16#63, + 16#BF, 16#05, 16#98, 16#DA, 16#48, 16#36, 16#1C, + 16#55, 16#D3, 16#9A, 16#69, 16#16, 16#3F, 16#A8, + 16#FD, 16#24, 16#CF, 16#5F, 16#83, 16#65, 16#5D, + 16#23, 16#DC, 16#A3, 16#AD, 16#96, 16#1C, 16#62, + 16#F3, 16#56, 16#20, 16#85, 16#52, 16#BB, 16#9E, + 16#D5, 16#29, 16#07, 16#70, 16#96, 16#96, 16#6D, + 16#67, 16#0C, 16#35, 16#4E, 16#4A, 16#BC, 16#98, + 16#04, 16#F1, 16#74, 16#6C, 16#08, 16#CA, 16#18, + 16#21, 16#7C, 16#32, 16#90, 16#5E, 16#46, 16#2E, + 16#36, 16#CE, 16#3B, 16#E3, 16#9E, 16#77, 16#2C, + 16#18, 16#0E, 16#86, 16#03, 16#9B, 16#27, 16#83, + 16#A2, 16#EC, 16#07, 16#A2, 16#8F, 16#B5, 16#C5, + 16#5D, 16#F0, 16#6F, 16#4C, 16#52, 16#C9, 16#DE, + 16#2B, 16#CB, 16#F6, 16#95, 16#58, 16#17, 16#18, + 16#39, 16#95, 16#49, 16#7C, 16#EA, 16#95, 16#6A, + 16#E5, 16#15, 16#D2, 16#26, 16#18, 16#98, 16#FA, + 16#05, 16#10, 16#15, 16#72, 16#8E, 16#5A, 16#8A, + 16#AA, 16#C4, 16#2D, 16#AD, 16#33, 16#17, 16#0D, + 16#04, 16#50, 16#7A, 16#33, 16#A8, 16#55, 16#21, + 16#AB, 16#DF, 16#1C, 16#BA, 16#64, 16#EC, 16#FB, + 16#85, 16#04, 16#58, 16#DB, 16#EF, 16#0A, 16#8A, + 16#EA, 16#71, 16#57, 16#5D, 16#06, 16#0C, 16#7D, + 16#B3, 16#97, 16#0F, 16#85, 16#A6, 16#E1, 16#E4, + 16#C7, 16#AB, 16#F5, 16#AE, 16#8C, 16#DB, 16#09, + 16#33, 16#D7, 16#1E, 16#8C, 16#94, 16#E0, 16#4A, + 16#25, 16#61, 16#9D, 16#CE, 16#E3, 16#D2, 16#26, + 16#1A, 16#D2, 16#EE, 16#6B, 16#F1, 16#2F, 16#FA, + 16#06, 16#D9, 16#8A, 16#08, 16#64, 16#D8, 16#76, + 16#02, 16#73, 16#3E, 16#C8, 16#6A, 16#64, 16#52, + 16#1F, 16#2B, 16#18, 16#17, 16#7B, 16#20, 16#0C, + 16#BB, 16#E1, 16#17, 16#57, 16#7A, 16#61, 16#5D, + 16#6C, 16#77, 16#09, 16#88, 16#C0, 16#BA, 16#D9, + 16#46, 16#E2, 16#08, 16#E2, 16#4F, 16#A0, 16#74, + 16#E5, 16#AB, 16#31, 16#43, 16#DB, 16#5B, 16#FC, + 16#E0, 16#FD, 16#10, 16#8E, 16#4B, 16#82, 16#D1, + 16#20, 16#A9, 16#21, 16#08, 16#01, 16#1A, 16#72, + 16#3C, 16#12, 16#A7, 16#87, 16#E6, 16#D7, 16#88, + 16#71, 16#9A, 16#10, 16#BD, 16#BA, 16#5B, 16#26, + 16#99, 16#C3, 16#27, 16#18, 16#6A, 16#F4, 16#E2, + 16#3C, 16#1A, 16#94, 16#68, 16#34, 16#B6, 16#15, + 16#0B, 16#DA, 16#25, 16#83, 16#E9, 16#CA, 16#2A, + 16#D4, 16#4C, 16#E8, 16#DB, 16#BB, 16#C2, 16#DB, + 16#04, 16#DE, 16#8E, 16#F9, 16#2E, 16#8E, 16#FC, + 16#14, 16#1F, 16#BE, 16#CA, 16#A6, 16#28, 16#7C, + 16#59, 16#47, 16#4E, 16#6B, 16#C0, 16#5D, 16#99, + 16#B2, 16#96, 16#4F, 16#A0, 16#90, 16#C3, 16#A2, + 16#23, 16#3B, 16#A1, 16#86, 16#51, 16#5B, 16#E7, + 16#ED, 16#1F, 16#61, 16#29, 16#70, 16#CE, 16#E2, + 16#D7, 16#AF, 16#B8, 16#1B, 16#DD, 16#76, 16#21, + 16#70, 16#48, 16#1C, 16#D0, 16#06, 16#91, 16#27, + 16#D5, 16#B0, 16#5A, 16#A9, 16#93, 16#B4, 16#EA, + 16#98, 16#8D, 16#8F, 16#DD, 16#C1, 16#86, 16#FF, + 16#B7, 16#DC, 16#90, 16#A6, 16#C0, 16#8F, 16#4D, + 16#F4, 16#35, 16#C9, 16#34, 16#06, 16#31, 16#99, + 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, + 16#FF>>). +-define(GENERATOR_4096, <<5>>). + +-define(PRIME_6144, <<16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, + 16#FF, 16#C9, 16#0F, 16#DA, 16#A2, 16#21, 16#68, + 16#C2, 16#34, 16#C4, 16#C6, 16#62, 16#8B, 16#80, + 16#DC, 16#1C, 16#D1, 16#29, 16#02, 16#4E, 16#08, + 16#8A, 16#67, 16#CC, 16#74, 16#02, 16#0B, 16#BE, + 16#A6, 16#3B, 16#13, 16#9B, 16#22, 16#51, 16#4A, + 16#08, 16#79, 16#8E, 16#34, 16#04, 16#DD, 16#EF, + 16#95, 16#19, 16#B3, 16#CD, 16#3A, 16#43, 16#1B, + 16#30, 16#2B, 16#0A, 16#6D, 16#F2, 16#5F, 16#14, + 16#37, 16#4F, 16#E1, 16#35, 16#6D, 16#6D, 16#51, + 16#C2, 16#45, 16#E4, 16#85, 16#B5, 16#76, 16#62, + 16#5E, 16#7E, 16#C6, 16#F4, 16#4C, 16#42, 16#E9, + 16#A6, 16#37, 16#ED, 16#6B, 16#0B, 16#FF, 16#5C, + 16#B6, 16#F4, 16#06, 16#B7, 16#ED, 16#EE, 16#38, + 16#6B, 16#FB, 16#5A, 16#89, 16#9F, 16#A5, 16#AE, + 16#9F, 16#24, 16#11, 16#7C, 16#4B, 16#1F, 16#E6, + 16#49, 16#28, 16#66, 16#51, 16#EC, 16#E4, 16#5B, + 16#3D, 16#C2, 16#00, 16#7C, 16#B8, 16#A1, 16#63, + 16#BF, 16#05, 16#98, 16#DA, 16#48, 16#36, 16#1C, + 16#55, 16#D3, 16#9A, 16#69, 16#16, 16#3F, 16#A8, + 16#FD, 16#24, 16#CF, 16#5F, 16#83, 16#65, 16#5D, + 16#23, 16#DC, 16#A3, 16#AD, 16#96, 16#1C, 16#62, + 16#F3, 16#56, 16#20, 16#85, 16#52, 16#BB, 16#9E, + 16#D5, 16#29, 16#07, 16#70, 16#96, 16#96, 16#6D, + 16#67, 16#0C, 16#35, 16#4E, 16#4A, 16#BC, 16#98, + 16#04, 16#F1, 16#74, 16#6C, 16#08, 16#CA, 16#18, + 16#21, 16#7C, 16#32, 16#90, 16#5E, 16#46, 16#2E, + 16#36, 16#CE, 16#3B, 16#E3, 16#9E, 16#77, 16#2C, + 16#18, 16#0E, 16#86, 16#03, 16#9B, 16#27, 16#83, + 16#A2, 16#EC, 16#07, 16#A2, 16#8F, 16#B5, 16#C5, + 16#5D, 16#F0, 16#6F, 16#4C, 16#52, 16#C9, 16#DE, + 16#2B, 16#CB, 16#F6, 16#95, 16#58, 16#17, 16#18, + 16#39, 16#95, 16#49, 16#7C, 16#EA, 16#95, 16#6A, + 16#E5, 16#15, 16#D2, 16#26, 16#18, 16#98, 16#FA, + 16#05, 16#10, 16#15, 16#72, 16#8E, 16#5A, 16#8A, + 16#AA, 16#C4, 16#2D, 16#AD, 16#33, 16#17, 16#0D, + 16#04, 16#50, 16#7A, 16#33, 16#A8, 16#55, 16#21, + 16#AB, 16#DF, 16#1C, 16#BA, 16#64, 16#EC, 16#FB, + 16#85, 16#04, 16#58, 16#DB, 16#EF, 16#0A, 16#8A, + 16#EA, 16#71, 16#57, 16#5D, 16#06, 16#0C, 16#7D, + 16#B3, 16#97, 16#0F, 16#85, 16#A6, 16#E1, 16#E4, + 16#C7, 16#AB, 16#F5, 16#AE, 16#8C, 16#DB, 16#09, + 16#33, 16#D7, 16#1E, 16#8C, 16#94, 16#E0, 16#4A, + 16#25, 16#61, 16#9D, 16#CE, 16#E3, 16#D2, 16#26, + 16#1A, 16#D2, 16#EE, 16#6B, 16#F1, 16#2F, 16#FA, + 16#06, 16#D9, 16#8A, 16#08, 16#64, 16#D8, 16#76, + 16#02, 16#73, 16#3E, 16#C8, 16#6A, 16#64, 16#52, + 16#1F, 16#2B, 16#18, 16#17, 16#7B, 16#20, 16#0C, + 16#BB, 16#E1, 16#17, 16#57, 16#7A, 16#61, 16#5D, + 16#6C, 16#77, 16#09, 16#88, 16#C0, 16#BA, 16#D9, + 16#46, 16#E2, 16#08, 16#E2, 16#4F, 16#A0, 16#74, + 16#E5, 16#AB, 16#31, 16#43, 16#DB, 16#5B, 16#FC, + 16#E0, 16#FD, 16#10, 16#8E, 16#4B, 16#82, 16#D1, + 16#20, 16#A9, 16#21, 16#08, 16#01, 16#1A, 16#72, + 16#3C, 16#12, 16#A7, 16#87, 16#E6, 16#D7, 16#88, + 16#71, 16#9A, 16#10, 16#BD, 16#BA, 16#5B, 16#26, + 16#99, 16#C3, 16#27, 16#18, 16#6A, 16#F4, 16#E2, + 16#3C, 16#1A, 16#94, 16#68, 16#34, 16#B6, 16#15, + 16#0B, 16#DA, 16#25, 16#83, 16#E9, 16#CA, 16#2A, + 16#D4, 16#4C, 16#E8, 16#DB, 16#BB, 16#C2, 16#DB, + 16#04, 16#DE, 16#8E, 16#F9, 16#2E, 16#8E, 16#FC, + 16#14, 16#1F, 16#BE, 16#CA, 16#A6, 16#28, 16#7C, + 16#59, 16#47, 16#4E, 16#6B, 16#C0, 16#5D, 16#99, + 16#B2, 16#96, 16#4F, 16#A0, 16#90, 16#C3, 16#A2, + 16#23, 16#3B, 16#A1, 16#86, 16#51, 16#5B, 16#E7, + 16#ED, 16#1F, 16#61, 16#29, 16#70, 16#CE, 16#E2, + 16#D7, 16#AF, 16#B8, 16#1B, 16#DD, 16#76, 16#21, + 16#70, 16#48, 16#1C, 16#D0, 16#06, 16#91, 16#27, + 16#D5, 16#B0, 16#5A, 16#A9, 16#93, 16#B4, 16#EA, + 16#98, 16#8D, 16#8F, 16#DD, 16#C1, 16#86, 16#FF, + 16#B7, 16#DC, 16#90, 16#A6, 16#C0, 16#8F, 16#4D, + 16#F4, 16#35, 16#C9, 16#34, 16#02, 16#84, 16#92, + 16#36, 16#C3, 16#FA, 16#B4, 16#D2, 16#7C, 16#70, + 16#26, 16#C1, 16#D4, 16#DC, 16#B2, 16#60, 16#26, + 16#46, 16#DE, 16#C9, 16#75, 16#1E, 16#76, 16#3D, + 16#BA, 16#37, 16#BD, 16#F8, 16#FF, 16#94, 16#06, + 16#AD, 16#9E, 16#53, 16#0E, 16#E5, 16#DB, 16#38, + 16#2F, 16#41, 16#30, 16#01, 16#AE, 16#B0, 16#6A, + 16#53, 16#ED, 16#90, 16#27, 16#D8, 16#31, 16#17, + 16#97, 16#27, 16#B0, 16#86, 16#5A, 16#89, 16#18, + 16#DA, 16#3E, 16#DB, 16#EB, 16#CF, 16#9B, 16#14, + 16#ED, 16#44, 16#CE, 16#6C, 16#BA, 16#CE, 16#D4, + 16#BB, 16#1B, 16#DB, 16#7F, 16#14, 16#47, 16#E6, + 16#CC, 16#25, 16#4B, 16#33, 16#20, 16#51, 16#51, + 16#2B, 16#D7, 16#AF, 16#42, 16#6F, 16#B8, 16#F4, + 16#01, 16#37, 16#8C, 16#D2, 16#BF, 16#59, 16#83, + 16#CA, 16#01, 16#C6, 16#4B, 16#92, 16#EC, 16#F0, + 16#32, 16#EA, 16#15, 16#D1, 16#72, 16#1D, 16#03, + 16#F4, 16#82, 16#D7, 16#CE, 16#6E, 16#74, 16#FE, + 16#F6, 16#D5, 16#5E, 16#70, 16#2F, 16#46, 16#98, + 16#0C, 16#82, 16#B5, 16#A8, 16#40, 16#31, 16#90, + 16#0B, 16#1C, 16#9E, 16#59, 16#E7, 16#C9, 16#7F, + 16#BE, 16#C7, 16#E8, 16#F3, 16#23, 16#A9, 16#7A, + 16#7E, 16#36, 16#CC, 16#88, 16#BE, 16#0F, 16#1D, + 16#45, 16#B7, 16#FF, 16#58, 16#5A, 16#C5, 16#4B, + 16#D4, 16#07, 16#B2, 16#2B, 16#41, 16#54, 16#AA, + 16#CC, 16#8F, 16#6D, 16#7E, 16#BF, 16#48, 16#E1, + 16#D8, 16#14, 16#CC, 16#5E, 16#D2, 16#0F, 16#80, + 16#37, 16#E0, 16#A7, 16#97, 16#15, 16#EE, 16#F2, + 16#9B, 16#E3, 16#28, 16#06, 16#A1, 16#D5, 16#8B, + 16#B7, 16#C5, 16#DA, 16#76, 16#F5, 16#50, 16#AA, + 16#3D, 16#8A, 16#1F, 16#BF, 16#F0, 16#EB, 16#19, + 16#CC, 16#B1, 16#A3, 16#13, 16#D5, 16#5C, 16#DA, + 16#56, 16#C9, 16#EC, 16#2E, 16#F2, 16#96, 16#32, + 16#38, 16#7F, 16#E8, 16#D7, 16#6E, 16#3C, 16#04, + 16#68, 16#04, 16#3E, 16#8F, 16#66, 16#3F, 16#48, + 16#60, 16#EE, 16#12, 16#BF, 16#2D, 16#5B, 16#0B, + 16#74, 16#74, 16#D6, 16#E6, 16#94, 16#F9, 16#1E, + 16#6D, 16#CC, 16#40, 16#24, 16#FF, 16#FF, 16#FF, + 16#FF, 16#FF, 16#FF, 16#FF, 16#FF>>). +-define(GENERATOR_6144, <<5>>). + +-define(PRIME_8192, <<16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, + 16#FF, 16#C9, 16#0F, 16#DA, 16#A2, 16#21, 16#68, + 16#C2, 16#34, 16#C4, 16#C6, 16#62, 16#8B, 16#80, + 16#DC, 16#1C, 16#D1, 16#29, 16#02, 16#4E, 16#08, + 16#8A, 16#67, 16#CC, 16#74, 16#02, 16#0B, 16#BE, + 16#A6, 16#3B, 16#13, 16#9B, 16#22, 16#51, 16#4A, + 16#08, 16#79, 16#8E, 16#34, 16#04, 16#DD, 16#EF, + 16#95, 16#19, 16#B3, 16#CD, 16#3A, 16#43, 16#1B, + 16#30, 16#2B, 16#0A, 16#6D, 16#F2, 16#5F, 16#14, + 16#37, 16#4F, 16#E1, 16#35, 16#6D, 16#6D, 16#51, + 16#C2, 16#45, 16#E4, 16#85, 16#B5, 16#76, 16#62, + 16#5E, 16#7E, 16#C6, 16#F4, 16#4C, 16#42, 16#E9, + 16#A6, 16#37, 16#ED, 16#6B, 16#0B, 16#FF, 16#5C, + 16#B6, 16#F4, 16#06, 16#B7, 16#ED, 16#EE, 16#38, + 16#6B, 16#FB, 16#5A, 16#89, 16#9F, 16#A5, 16#AE, + 16#9F, 16#24, 16#11, 16#7C, 16#4B, 16#1F, 16#E6, + 16#49, 16#28, 16#66, 16#51, 16#EC, 16#E4, 16#5B, + 16#3D, 16#C2, 16#00, 16#7C, 16#B8, 16#A1, 16#63, + 16#BF, 16#05, 16#98, 16#DA, 16#48, 16#36, 16#1C, + 16#55, 16#D3, 16#9A, 16#69, 16#16, 16#3F, 16#A8, + 16#FD, 16#24, 16#CF, 16#5F, 16#83, 16#65, 16#5D, + 16#23, 16#DC, 16#A3, 16#AD, 16#96, 16#1C, 16#62, + 16#F3, 16#56, 16#20, 16#85, 16#52, 16#BB, 16#9E, + 16#D5, 16#29, 16#07, 16#70, 16#96, 16#96, 16#6D, + 16#67, 16#0C, 16#35, 16#4E, 16#4A, 16#BC, 16#98, + 16#04, 16#F1, 16#74, 16#6C, 16#08, 16#CA, 16#18, + 16#21, 16#7C, 16#32, 16#90, 16#5E, 16#46, 16#2E, + 16#36, 16#CE, 16#3B, 16#E3, 16#9E, 16#77, 16#2C, + 16#18, 16#0E, 16#86, 16#03, 16#9B, 16#27, 16#83, + 16#A2, 16#EC, 16#07, 16#A2, 16#8F, 16#B5, 16#C5, + 16#5D, 16#F0, 16#6F, 16#4C, 16#52, 16#C9, 16#DE, + 16#2B, 16#CB, 16#F6, 16#95, 16#58, 16#17, 16#18, + 16#39, 16#95, 16#49, 16#7C, 16#EA, 16#95, 16#6A, + 16#E5, 16#15, 16#D2, 16#26, 16#18, 16#98, 16#FA, + 16#05, 16#10, 16#15, 16#72, 16#8E, 16#5A, 16#8A, + 16#AA, 16#C4, 16#2D, 16#AD, 16#33, 16#17, 16#0D, + 16#04, 16#50, 16#7A, 16#33, 16#A8, 16#55, 16#21, + 16#AB, 16#DF, 16#1C, 16#BA, 16#64, 16#EC, 16#FB, + 16#85, 16#04, 16#58, 16#DB, 16#EF, 16#0A, 16#8A, + 16#EA, 16#71, 16#57, 16#5D, 16#06, 16#0C, 16#7D, + 16#B3, 16#97, 16#0F, 16#85, 16#A6, 16#E1, 16#E4, + 16#C7, 16#AB, 16#F5, 16#AE, 16#8C, 16#DB, 16#09, + 16#33, 16#D7, 16#1E, 16#8C, 16#94, 16#E0, 16#4A, + 16#25, 16#61, 16#9D, 16#CE, 16#E3, 16#D2, 16#26, + 16#1A, 16#D2, 16#EE, 16#6B, 16#F1, 16#2F, 16#FA, + 16#06, 16#D9, 16#8A, 16#08, 16#64, 16#D8, 16#76, + 16#02, 16#73, 16#3E, 16#C8, 16#6A, 16#64, 16#52, + 16#1F, 16#2B, 16#18, 16#17, 16#7B, 16#20, 16#0C, + 16#BB, 16#E1, 16#17, 16#57, 16#7A, 16#61, 16#5D, + 16#6C, 16#77, 16#09, 16#88, 16#C0, 16#BA, 16#D9, + 16#46, 16#E2, 16#08, 16#E2, 16#4F, 16#A0, 16#74, + 16#E5, 16#AB, 16#31, 16#43, 16#DB, 16#5B, 16#FC, + 16#E0, 16#FD, 16#10, 16#8E, 16#4B, 16#82, 16#D1, + 16#20, 16#A9, 16#21, 16#08, 16#01, 16#1A, 16#72, + 16#3C, 16#12, 16#A7, 16#87, 16#E6, 16#D7, 16#88, + 16#71, 16#9A, 16#10, 16#BD, 16#BA, 16#5B, 16#26, + 16#99, 16#C3, 16#27, 16#18, 16#6A, 16#F4, 16#E2, + 16#3C, 16#1A, 16#94, 16#68, 16#34, 16#B6, 16#15, + 16#0B, 16#DA, 16#25, 16#83, 16#E9, 16#CA, 16#2A, + 16#D4, 16#4C, 16#E8, 16#DB, 16#BB, 16#C2, 16#DB, + 16#04, 16#DE, 16#8E, 16#F9, 16#2E, 16#8E, 16#FC, + 16#14, 16#1F, 16#BE, 16#CA, 16#A6, 16#28, 16#7C, + 16#59, 16#47, 16#4E, 16#6B, 16#C0, 16#5D, 16#99, + 16#B2, 16#96, 16#4F, 16#A0, 16#90, 16#C3, 16#A2, + 16#23, 16#3B, 16#A1, 16#86, 16#51, 16#5B, 16#E7, + 16#ED, 16#1F, 16#61, 16#29, 16#70, 16#CE, 16#E2, + 16#D7, 16#AF, 16#B8, 16#1B, 16#DD, 16#76, 16#21, + 16#70, 16#48, 16#1C, 16#D0, 16#06, 16#91, 16#27, + 16#D5, 16#B0, 16#5A, 16#A9, 16#93, 16#B4, 16#EA, + 16#98, 16#8D, 16#8F, 16#DD, 16#C1, 16#86, 16#FF, + 16#B7, 16#DC, 16#90, 16#A6, 16#C0, 16#8F, 16#4D, + 16#F4, 16#35, 16#C9, 16#34, 16#02, 16#84, 16#92, + 16#36, 16#C3, 16#FA, 16#B4, 16#D2, 16#7C, 16#70, + 16#26, 16#C1, 16#D4, 16#DC, 16#B2, 16#60, 16#26, + 16#46, 16#DE, 16#C9, 16#75, 16#1E, 16#76, 16#3D, + 16#BA, 16#37, 16#BD, 16#F8, 16#FF, 16#94, 16#06, + 16#AD, 16#9E, 16#53, 16#0E, 16#E5, 16#DB, 16#38, + 16#2F, 16#41, 16#30, 16#01, 16#AE, 16#B0, 16#6A, + 16#53, 16#ED, 16#90, 16#27, 16#D8, 16#31, 16#17, + 16#97, 16#27, 16#B0, 16#86, 16#5A, 16#89, 16#18, + 16#DA, 16#3E, 16#DB, 16#EB, 16#CF, 16#9B, 16#14, + 16#ED, 16#44, 16#CE, 16#6C, 16#BA, 16#CE, 16#D4, + 16#BB, 16#1B, 16#DB, 16#7F, 16#14, 16#47, 16#E6, + 16#CC, 16#25, 16#4B, 16#33, 16#20, 16#51, 16#51, + 16#2B, 16#D7, 16#AF, 16#42, 16#6F, 16#B8, 16#F4, + 16#01, 16#37, 16#8C, 16#D2, 16#BF, 16#59, 16#83, + 16#CA, 16#01, 16#C6, 16#4B, 16#92, 16#EC, 16#F0, + 16#32, 16#EA, 16#15, 16#D1, 16#72, 16#1D, 16#03, + 16#F4, 16#82, 16#D7, 16#CE, 16#6E, 16#74, 16#FE, + 16#F6, 16#D5, 16#5E, 16#70, 16#2F, 16#46, 16#98, + 16#0C, 16#82, 16#B5, 16#A8, 16#40, 16#31, 16#90, + 16#0B, 16#1C, 16#9E, 16#59, 16#E7, 16#C9, 16#7F, + 16#BE, 16#C7, 16#E8, 16#F3, 16#23, 16#A9, 16#7A, + 16#7E, 16#36, 16#CC, 16#88, 16#BE, 16#0F, 16#1D, + 16#45, 16#B7, 16#FF, 16#58, 16#5A, 16#C5, 16#4B, + 16#D4, 16#07, 16#B2, 16#2B, 16#41, 16#54, 16#AA, + 16#CC, 16#8F, 16#6D, 16#7E, 16#BF, 16#48, 16#E1, + 16#D8, 16#14, 16#CC, 16#5E, 16#D2, 16#0F, 16#80, + 16#37, 16#E0, 16#A7, 16#97, 16#15, 16#EE, 16#F2, + 16#9B, 16#E3, 16#28, 16#06, 16#A1, 16#D5, 16#8B, + 16#B7, 16#C5, 16#DA, 16#76, 16#F5, 16#50, 16#AA, + 16#3D, 16#8A, 16#1F, 16#BF, 16#F0, 16#EB, 16#19, + 16#CC, 16#B1, 16#A3, 16#13, 16#D5, 16#5C, 16#DA, + 16#56, 16#C9, 16#EC, 16#2E, 16#F2, 16#96, 16#32, + 16#38, 16#7F, 16#E8, 16#D7, 16#6E, 16#3C, 16#04, + 16#68, 16#04, 16#3E, 16#8F, 16#66, 16#3F, 16#48, + 16#60, 16#EE, 16#12, 16#BF, 16#2D, 16#5B, 16#0B, + 16#74, 16#74, 16#D6, 16#E6, 16#94, 16#F9, 16#1E, + 16#6D, 16#BE, 16#11, 16#59, 16#74, 16#A3, 16#92, + 16#6F, 16#12, 16#FE, 16#E5, 16#E4, 16#38, 16#77, + 16#7C, 16#B6, 16#A9, 16#32, 16#DF, 16#8C, 16#D8, + 16#BE, 16#C4, 16#D0, 16#73, 16#B9, 16#31, 16#BA, + 16#3B, 16#C8, 16#32, 16#B6, 16#8D, 16#9D, 16#D3, + 16#00, 16#74, 16#1F, 16#A7, 16#BF, 16#8A, 16#FC, + 16#47, 16#ED, 16#25, 16#76, 16#F6, 16#93, 16#6B, + 16#A4, 16#24, 16#66, 16#3A, 16#AB, 16#63, 16#9C, + 16#5A, 16#E4, 16#F5, 16#68, 16#34, 16#23, 16#B4, + 16#74, 16#2B, 16#F1, 16#C9, 16#78, 16#23, 16#8F, + 16#16, 16#CB, 16#E3, 16#9D, 16#65, 16#2D, 16#E3, + 16#FD, 16#B8, 16#BE, 16#FC, 16#84, 16#8A, 16#D9, + 16#22, 16#22, 16#2E, 16#04, 16#A4, 16#03, 16#7C, + 16#07, 16#13, 16#EB, 16#57, 16#A8, 16#1A, 16#23, + 16#F0, 16#C7, 16#34, 16#73, 16#FC, 16#64, 16#6C, + 16#EA, 16#30, 16#6B, 16#4B, 16#CB, 16#C8, 16#86, + 16#2F, 16#83, 16#85, 16#DD, 16#FA, 16#9D, 16#4B, + 16#7F, 16#A2, 16#C0, 16#87, 16#E8, 16#79, 16#68, + 16#33, 16#03, 16#ED, 16#5B, 16#DD, 16#3A, 16#06, + 16#2B, 16#3C, 16#F5, 16#B3, 16#A2, 16#78, 16#A6, + 16#6D, 16#2A, 16#13, 16#F8, 16#3F, 16#44, 16#F8, + 16#2D, 16#DF, 16#31, 16#0E, 16#E0, 16#74, 16#AB, + 16#6A, 16#36, 16#45, 16#97, 16#E8, 16#99, 16#A0, + 16#25, 16#5D, 16#C1, 16#64, 16#F3, 16#1C, 16#C5, + 16#08, 16#46, 16#85, 16#1D, 16#F9, 16#AB, 16#48, + 16#19, 16#5D, 16#ED, 16#7E, 16#A1, 16#B1, 16#D5, + 16#10, 16#BD, 16#7E, 16#E7, 16#4D, 16#73, 16#FA, + 16#F3, 16#6B, 16#C3, 16#1E, 16#CF, 16#A2, 16#68, + 16#35, 16#90, 16#46, 16#F4, 16#EB, 16#87, 16#9F, + 16#92, 16#40, 16#09, 16#43, 16#8B, 16#48, 16#1C, + 16#6C, 16#D7, 16#88, 16#9A, 16#00, 16#2E, 16#D5, + 16#EE, 16#38, 16#2B, 16#C9, 16#19, 16#0D, 16#A6, + 16#FC, 16#02, 16#6E, 16#47, 16#95, 16#58, 16#E4, + 16#47, 16#56, 16#77, 16#E9, 16#AA, 16#9E, 16#30, + 16#50, 16#E2, 16#76, 16#56, 16#94, 16#DF, 16#C8, + 16#1F, 16#56, 16#E8, 16#80, 16#B9, 16#6E, 16#71, + 16#60, 16#C9, 16#80, 16#DD, 16#98, 16#ED, 16#D3, + 16#DF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, + 16#FF, 16#FF>>). +-define(GENERATOR_8192, <<19>>). + +get_srp_params(srp_1024) -> {?GENERATOR_1024, ?PRIME_1024}; +get_srp_params(srp_1536) -> {?GENERATOR_1536, ?PRIME_1536}; +get_srp_params(srp_2048) -> {?GENERATOR_2048, ?PRIME_2048}; +get_srp_params(srp_3072) -> {?GENERATOR_3072, ?PRIME_3072}; +get_srp_params(srp_4096) -> {?GENERATOR_4096, ?PRIME_4096}; +get_srp_params(srp_6144) -> {?GENERATOR_6144, ?PRIME_6144}; +get_srp_params(srp_8192) -> {?GENERATOR_8192, ?PRIME_8192}. + +check_srp_params(?GENERATOR_1024, ?PRIME_1024) -> ok; +check_srp_params(?GENERATOR_1536, ?PRIME_1536) -> ok; +check_srp_params(?GENERATOR_2048, ?PRIME_2048) -> ok; +check_srp_params(?GENERATOR_3072, ?PRIME_3072) -> ok; +check_srp_params(?GENERATOR_4096, ?PRIME_4096) -> ok; +check_srp_params(?GENERATOR_6144, ?PRIME_6144) -> ok; +check_srp_params(?GENERATOR_8192, ?PRIME_8192) -> ok; +check_srp_params(_Generator, _Prime) -> + not_accepted. diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl index a11c5b8c0c..013c27ebb5 100644 --- a/lib/ssl/src/ssl_ssl3.erl +++ b/lib/ssl/src/ssl_ssl3.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -154,9 +154,9 @@ suites() -> %%-------------------------------------------------------------------- hash(?MD5, Data) -> - crypto:md5(Data); + crypto:hash(md5, Data); hash(?SHA, Data) -> - crypto:sha(Data). + crypto:hash(sha, Data). %%pad_1(?NULL) -> %% ""; @@ -198,6 +198,6 @@ gen(_Secret, _All, Wanted, Len, _C, _N, Acc) when Wanted =< Len -> Block; gen(Secret, All, Wanted, Len, C, N, Acc) -> Prefix = lists:duplicate(N, C), - SHA = crypto:sha([Prefix, All]), - MD5 = crypto:md5([Secret, SHA]), + SHA = crypto:hash(sha, [Prefix, All]), + MD5 = crypto:hash(md5, [Secret, SHA]), gen(Secret, All, Wanted, Len + 16, C+1, N+1, [MD5 | Acc]). diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index 41dc1bf0dc..8ab66d0627 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -29,7 +29,8 @@ -include("ssl_record.hrl"). -export([master_secret/4, finished/5, certificate_verify/3, mac_hash/7, - setup_keys/8, suites/1, prf/5]). + setup_keys/8, suites/1, prf/5, + ecc_curves/1, oid_to_enum/1, enum_to_oid/1]). %%==================================================================== %% Internal application API @@ -57,8 +58,8 @@ finished(Role, Version, PrfAlgo, MasterSecret, Handshake) %% verify_data %% PRF(master_secret, finished_label, MD5(handshake_messages) + %% SHA-1(handshake_messages)) [0..11]; - MD5 = crypto:md5(Handshake), - SHA = crypto:sha(Handshake), + MD5 = crypto:hash(md5, Handshake), + SHA = crypto:hash(sha, Handshake), prf(?MD5SHA, MasterSecret, finished_label(Role), [MD5, SHA], 12); finished(Role, Version, PrfAlgo, MasterSecret, Handshake) @@ -76,8 +77,8 @@ finished(Role, Version, PrfAlgo, MasterSecret, Handshake) -spec certificate_verify(md5sha | sha, integer(), [binary()]) -> binary(). certificate_verify(md5sha, _Version, Handshake) -> - MD5 = crypto:md5(Handshake), - SHA = crypto:sha(Handshake), + MD5 = crypto:hash(md5, Handshake), + SHA = crypto:hash(sha, Handshake), <<MD5/binary, SHA/binary>>; certificate_verify(HashAlgo, _Version, Handshake) -> @@ -183,24 +184,95 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, -spec suites(1|2|3) -> [cipher_suite()]. suites(Minor) when Minor == 1; Minor == 2-> + case sufficent_ec_support() of + true -> + all_suites(Minor); + false -> + no_ec_suites(Minor) + end; + +suites(Minor) when Minor == 3 -> + case sufficent_ec_support() of + true -> + all_suites(3) ++ all_suites(2); + false -> + no_ec_suites(3) ++ no_ec_suites(2) + end. + +all_suites(Minor) when Minor == 1; Minor == 2-> [ + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, ?TLS_RSA_WITH_AES_256_CBC_SHA, + + ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, ?TLS_RSA_WITH_3DES_EDE_CBC_SHA, + + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, ?TLS_RSA_WITH_AES_128_CBC_SHA, - %%?TLS_RSA_WITH_IDEA_CBC_SHA, + + ?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + ?TLS_ECDHE_RSA_WITH_RC4_128_SHA, ?TLS_RSA_WITH_RC4_128_SHA, ?TLS_RSA_WITH_RC4_128_MD5, ?TLS_DHE_RSA_WITH_DES_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + ?TLS_ECDH_RSA_WITH_RC4_128_SHA, + ?TLS_RSA_WITH_DES_CBC_SHA - ]; + ]; +all_suites(3) -> + [ + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, + ?TLS_RSA_WITH_AES_256_CBC_SHA256, + + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, + ?TLS_RSA_WITH_AES_128_CBC_SHA256 + ]. -suites(Minor) when Minor == 3 -> +no_ec_suites(Minor) when Minor == 1; Minor == 2-> + [ + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + ?TLS_RSA_WITH_AES_256_CBC_SHA, + ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + ?TLS_RSA_WITH_AES_128_CBC_SHA, + ?TLS_RSA_WITH_RC4_128_SHA, + ?TLS_RSA_WITH_RC4_128_MD5, + ?TLS_DHE_RSA_WITH_DES_CBC_SHA, + ?TLS_RSA_WITH_DES_CBC_SHA + ]; +no_ec_suites(3) -> [ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, @@ -208,9 +280,7 @@ suites(Minor) when Minor == 3 -> ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, ?TLS_RSA_WITH_AES_128_CBC_SHA256 - %% ?TLS_DH_anon_WITH_AES_128_CBC_SHA256, - %% ?TLS_DH_anon_WITH_AES_256_CBC_SHA256 - ] ++ suites(2). + ]. %%-------------------------------------------------------------------- %%% Internal functions @@ -218,16 +288,8 @@ suites(Minor) when Minor == 3 -> %%%% HMAC and the Pseudorandom Functions RFC 2246 & 4346 - 5.%%%% hmac_hash(?NULL, _, _) -> <<>>; -hmac_hash(?MD5, Key, Value) -> - crypto:md5_mac(Key, Value); -hmac_hash(?SHA, Key, Value) -> - crypto:sha_mac(Key, Value); -hmac_hash(?SHA256, Key, Value) -> - crypto:sha256_mac(Key, Value); -hmac_hash(?SHA384, Key, Value) -> - crypto:sha384_mac(Key, Value); -hmac_hash(?SHA512, Key, Value) -> - crypto:sha512_mac(Key, Value). +hmac_hash(Alg, Key, Value) -> + crypto:hmac(mac_algo(Alg), Key, Value). mac_algo(?MD5) -> md5; mac_algo(?SHA) -> sha; @@ -303,3 +365,68 @@ finished_label(client) -> <<"client finished">>; finished_label(server) -> <<"server finished">>. + +%% list ECC curves in prefered order +ecc_curves(_Minor) -> + [?sect571r1,?sect571k1,?secp521r1,?sect409k1,?sect409r1, + ?secp384r1,?sect283k1,?sect283r1,?secp256k1,?secp256r1, + ?sect239k1,?sect233k1,?sect233r1,?secp224k1,?secp224r1, + ?sect193r1,?sect193r2,?secp192k1,?secp192r1,?sect163k1, + ?sect163r1,?sect163r2,?secp160k1,?secp160r1,?secp160r2]. + +%% ECC curves from draft-ietf-tls-ecc-12.txt (Oct. 17, 2005) +oid_to_enum(?sect163k1) -> 1; +oid_to_enum(?sect163r1) -> 2; +oid_to_enum(?sect163r2) -> 3; +oid_to_enum(?sect193r1) -> 4; +oid_to_enum(?sect193r2) -> 5; +oid_to_enum(?sect233k1) -> 6; +oid_to_enum(?sect233r1) -> 7; +oid_to_enum(?sect239k1) -> 8; +oid_to_enum(?sect283k1) -> 9; +oid_to_enum(?sect283r1) -> 10; +oid_to_enum(?sect409k1) -> 11; +oid_to_enum(?sect409r1) -> 12; +oid_to_enum(?sect571k1) -> 13; +oid_to_enum(?sect571r1) -> 14; +oid_to_enum(?secp160k1) -> 15; +oid_to_enum(?secp160r1) -> 16; +oid_to_enum(?secp160r2) -> 17; +oid_to_enum(?secp192k1) -> 18; +oid_to_enum(?secp192r1) -> 19; +oid_to_enum(?secp224k1) -> 20; +oid_to_enum(?secp224r1) -> 21; +oid_to_enum(?secp256k1) -> 22; +oid_to_enum(?secp256r1) -> 23; +oid_to_enum(?secp384r1) -> 24; +oid_to_enum(?secp521r1) -> 25. + +enum_to_oid(1) -> ?sect163k1; +enum_to_oid(2) -> ?sect163r1; +enum_to_oid(3) -> ?sect163r2; +enum_to_oid(4) -> ?sect193r1; +enum_to_oid(5) -> ?sect193r2; +enum_to_oid(6) -> ?sect233k1; +enum_to_oid(7) -> ?sect233r1; +enum_to_oid(8) -> ?sect239k1; +enum_to_oid(9) -> ?sect283k1; +enum_to_oid(10) -> ?sect283r1; +enum_to_oid(11) -> ?sect409k1; +enum_to_oid(12) -> ?sect409r1; +enum_to_oid(13) -> ?sect571k1; +enum_to_oid(14) -> ?sect571r1; +enum_to_oid(15) -> ?secp160k1; +enum_to_oid(16) -> ?secp160r1; +enum_to_oid(17) -> ?secp160r2; +enum_to_oid(18) -> ?secp192k1; +enum_to_oid(19) -> ?secp192r1; +enum_to_oid(20) -> ?secp224k1; +enum_to_oid(21) -> ?secp224r1; +enum_to_oid(22) -> ?secp256k1; +enum_to_oid(23) -> ?secp256r1; +enum_to_oid(24) -> ?secp384r1; +enum_to_oid(25) -> ?secp521r1. + +sufficent_ec_support() -> + CryptoSupport = crypto:supports(), + proplists:get_bool(ecdh, proplists:get_value(public_keys, CryptoSupport)). diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl index a8476b104f..a22af6b960 100644 --- a/lib/ssl/src/ssl_tls_dist_proxy.erl +++ b/lib/ssl/src/ssl_tls_dist_proxy.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2012. All Rights Reserved. +%% Copyright Ericsson AB 2011-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -89,7 +89,7 @@ handle_call({connect, Ip, Port}, {From, _}, State) -> ok -> flush_old_controller(From, Socket), {reply, Res, State} - end; + end; {Pid, Error} -> {reply, Error, State} end; @@ -115,13 +115,13 @@ code_change(_OldVsn, St, _Extra) -> get_tcp_address(Socket) -> case inet:sockname(Socket) of {ok, Address} -> - {ok, Host} = inet:gethostname(), + {ok, Host} = inet:gethostname(), NetAddress = #net_address{ - address = Address, - host = Host, - protocol = proxy, - family = inet - }, + address = Address, + host = Host, + protocol = proxy, + family = inet + }, {ok, NetAddress}; {error, _} = Error -> Error end. @@ -129,17 +129,17 @@ get_tcp_address(Socket) -> accept_loop(Proxy, erts = Type, Listen, Extra) -> process_flag(priority, max), case gen_tcp:accept(Listen) of - {ok, Socket} -> - Extra ! {accept,self(),Socket,inet,proxy}, - receive - {_Kernel, controller, Pid} -> - ok = gen_tcp:controlling_process(Socket, Pid), - flush_old_controller(Pid, Socket), - Pid ! {self(), controller}; - {_Kernel, unsupported_protocol} -> - exit(unsupported_protocol) - end; - Error -> + {ok, Socket} -> + Extra ! {accept,self(),Socket,inet,proxy}, + receive + {_Kernel, controller, Pid} -> + ok = gen_tcp:controlling_process(Socket, Pid), + flush_old_controller(Pid, Socket), + Pid ! {self(), controller}; + {_Kernel, unsupported_protocol} -> + exit(unsupported_protocol) + end; + Error -> exit(Error) end, accept_loop(Proxy, Type, Listen, Extra); @@ -242,7 +242,7 @@ loop_conn(World, Erts) -> ssl:close(World); {ssl_closed, World} -> gen_tcp:close(Erts) - end. + end. get_ssl_options(Type) -> case init:get_argument(ssl_dist_opt) of @@ -255,7 +255,7 @@ get_ssl_options(Type) -> ssl_options(_,[]) -> []; ssl_options(server, ["client_" ++ _, _Value |T]) -> - ssl_options(server,T); + ssl_options(server,T); ssl_options(client, ["server_" ++ _, _Value|T]) -> ssl_options(client,T); ssl_options(server, ["server_certfile", Value|T]) -> @@ -265,7 +265,7 @@ ssl_options(client, ["client_certfile", Value | T]) -> ssl_options(server, ["server_cacertfile", Value|T]) -> [{cacertfile, Value} | ssl_options(server,T)]; ssl_options(client, ["client_cacertfile", Value|T]) -> - [{cacertfile, Value} | ssl_options(client,T)]; + [{cacertfile, Value} | ssl_options(client,T)]; ssl_options(server, ["server_keyfile", Value|T]) -> [{keyfile, Value} | ssl_options(server,T)]; ssl_options(client, ["client_keyfile", Value|T]) -> @@ -277,7 +277,7 @@ ssl_options(client, ["client_password", Value|T]) -> ssl_options(server, ["server_verify", Value|T]) -> [{verify, atomize(Value)} | ssl_options(server,T)]; ssl_options(client, ["client_verify", Value|T]) -> - [{verify, atomize(Value)} | ssl_options(client,T)]; + [{verify, atomize(Value)} | ssl_options(client,T)]; ssl_options(server, ["server_reuse_sessions", Value|T]) -> [{reuse_sessions, atomize(Value)} | ssl_options(server,T)]; ssl_options(client, ["client_reuse_sessions", Value|T]) -> @@ -295,11 +295,11 @@ ssl_options(server, ["server_hibernate_after", Value|T]) -> ssl_options(client, ["client_hibernate_after", Value|T]) -> [{hibernate_after, list_to_integer(Value)} | ssl_options(client,T)]; ssl_options(server, ["server_ciphers", Value|T]) -> - [{ciphers, Value} | ssl_options(server,T)]; + [{ciphers, Value} | ssl_options(server,T)]; ssl_options(client, ["client_ciphers", Value|T]) -> [{ciphers, Value} | ssl_options(client,T)]; ssl_options(server, ["server_dhfile", Value|T]) -> - [{dhfile, Value} | ssl_options(server,T)]; + [{dhfile, Value} | ssl_options(server,T)]; ssl_options(server, ["server_fail_if_no_peer_cert", Value|T]) -> [{fail_if_no_peer_cert, atomize(Value)} | ssl_options(server,T)]; ssl_options(_,_) -> diff --git a/lib/ssl/src/tls.erl b/lib/ssl/src/tls.erl new file mode 100644 index 0000000000..b220a48f73 --- /dev/null +++ b/lib/ssl/src/tls.erl @@ -0,0 +1,1043 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% + +%%% Purpose : Main API module for SSL. + +-module(tls). + +-export([start/0, start/1, stop/0, transport_accept/1, + transport_accept/2, ssl_accept/1, ssl_accept/2, ssl_accept/3, + cipher_suites/0, cipher_suites/1, suite_definition/1, + close/1, shutdown/2, + connect/3, connect/2, connect/4, connection_info/1, + controlling_process/2, listen/2, peername/1, peercert/1, + recv/2, recv/3, send/2, getopts/2, setopts/2, sockname/1, + versions/0, session_info/1, format_error/1, + renegotiate/1, prf/5, clear_pem_cache/0, random_bytes/1, negotiated_next_protocol/1]). + +-include("ssl_internal.hrl"). +-include("ssl_record.hrl"). +-include("ssl_cipher.hrl"). +-include("ssl_handshake.hrl"). +-include("ssl_srp.hrl"). + +-include_lib("public_key/include/public_key.hrl"). + +%% Visible in API +-export_type([connect_option/0, listen_option/0, ssl_option/0, transport_option/0, + erl_cipher_suite/0, %% From ssl_cipher.hrl + tls_atom_version/0, %% From ssl_internal.hrl + prf_random/0, sslsocket/0]). + +-record(config, {ssl, %% SSL parameters + inet_user, %% User set inet options + emulated, %% #socket_option{} emulated + inet_ssl, %% inet options for internal ssl socket + cb %% Callback info + }). + +-type sslsocket() :: #sslsocket{}. +-type connect_option() :: socket_connect_option() | ssl_option() | transport_option(). +-type socket_connect_option() :: gen_tcp:connect_option(). +-type listen_option() :: socket_listen_option() | ssl_option() | transport_option(). +-type socket_listen_option() :: gen_tcp:listen_option(). + +-type ssl_option() :: {verify, verify_type()} | + {verify_fun, {fun(), InitialUserState::term()}} | + {fail_if_no_peer_cert, boolean()} | {depth, integer()} | + {cert, Der::binary()} | {certfile, path()} | {key, Der::binary()} | + {keyfile, path()} | {password, string()} | {cacerts, [Der::binary()]} | + {cacertfile, path()} | {dh, Der::binary()} | {dhfile, path()} | + {user_lookup_fun, {fun(), InitialUserState::term()}} | + {psk_identity, string()} | + {srp_identity, {string(), string()}} | + {ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | + {reuse_session, fun()} | {hibernate_after, integer()|undefined} | + {next_protocols_advertised, list(binary())} | + {client_preferred_next_protocols, binary(), client | server, list(binary())}. + +-type verify_type() :: verify_none | verify_peer. +-type path() :: string(). +-type ciphers() :: [erl_cipher_suite()] | + string(). % (according to old API) +-type ssl_imp() :: new | old. + +-type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(), + ClosedTag::atom(), ErrTag::atom()}}. +-type prf_random() :: client_random | server_random. + +%%-------------------------------------------------------------------- +-spec start() -> ok | {error, reason()}. +-spec start(permanent | transient | temporary) -> ok | {error, reason()}. +%% +%% Description: Utility function that starts the ssl, +%% crypto and public_key applications. Default type +%% is temporary. see application(3) +%%-------------------------------------------------------------------- +start() -> + application:start(crypto), + application:start(asn1), + application:start(public_key), + application:start(ssl). + +start(Type) -> + application:start(crypto, Type), + application:start(asn1), + application:start(public_key, Type), + application:start(ssl, Type). + +%%-------------------------------------------------------------------- +-spec stop() -> ok. +%% +%% Description: Stops the ssl application. +%%-------------------------------------------------------------------- +stop() -> + application:stop(ssl). + +%%-------------------------------------------------------------------- +-spec connect(host() | port(), [connect_option()]) -> {ok, #sslsocket{}} | + {error, reason()}. +-spec connect(host() | port(), [connect_option()] | inet:port_number(), + timeout() | list()) -> + {ok, #sslsocket{}} | {error, reason()}. +-spec connect(host() | port(), inet:port_number(), list(), timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. + +%% +%% Description: Connect to an ssl server. +%%-------------------------------------------------------------------- +connect(Socket, SslOptions) when is_port(Socket) -> + connect(Socket, SslOptions, infinity). + +connect(Socket, SslOptions0, Timeout) when is_port(Socket) -> + {Transport,_,_,_} = proplists:get_value(cb_info, SslOptions0, + {gen_tcp, tcp, tcp_closed, tcp_error}), + EmulatedOptions = emulated_options(), + {ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions), + try handle_options(SslOptions0 ++ SocketValues, client) of + {ok, #config{cb = CbInfo, ssl = SslOptions, emulated = EmOpts}} -> + + ok = ssl_socket:setopts(Transport, Socket, internal_inet_values()), + case ssl_socket:peername(Transport, Socket) of + {ok, {Address, Port}} -> + tls_connection:connect(Address, Port, Socket, + {SslOptions, EmOpts}, + self(), CbInfo, Timeout); + {error, Error} -> + {error, Error} + end + catch + _:{error, Reason} -> + {error, Reason} + end; + +connect(Host, Port, Options) -> + connect(Host, Port, Options, infinity). + +connect(Host, Port, Options, Timeout) -> + try handle_options(Options, client) of + {ok, Config} -> + do_connect(Host,Port,Config,Timeout) + catch + throw:Error -> + Error + end. + +%%-------------------------------------------------------------------- +-spec listen(inet:port_number(), [listen_option()]) ->{ok, #sslsocket{}} | {error, reason()}. + +%% +%% Description: Creates an ssl listen socket. +%%-------------------------------------------------------------------- +listen(_Port, []) -> + {error, nooptions}; +listen(Port, Options0) -> + try + {ok, Config} = handle_options(Options0, server), + #config{cb = {Transport, _, _, _}, inet_user = Options} = Config, + case Transport:listen(Port, Options) of + {ok, ListenSocket} -> + {ok, #sslsocket{pid = {ListenSocket, Config}}}; + Err = {error, _} -> + Err + end + catch + Error = {error, _} -> + Error + end. +%%-------------------------------------------------------------------- +-spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}} | + {error, reason()}. +-spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | + {error, reason()}. +%% +%% Description: Performs transport accept on an ssl listen socket +%%-------------------------------------------------------------------- +transport_accept(ListenSocket) -> + transport_accept(ListenSocket, infinity). + +transport_accept(#sslsocket{pid = {ListenSocket, #config{cb = CbInfo, ssl = SslOpts}}}, Timeout) -> + + %% The setopt could have been invoked on the listen socket + %% and options should be inherited. + EmOptions = emulated_options(), + {Transport,_,_, _} = CbInfo, + {ok, SocketValues} = ssl_socket:getopts(Transport, ListenSocket, EmOptions), + ok = ssl_socket:setopts(Transport, ListenSocket, internal_inet_values()), + case Transport:accept(ListenSocket, Timeout) of + {ok, Socket} -> + ok = ssl_socket:setopts(Transport, ListenSocket, SocketValues), + {ok, Port} = ssl_socket:port(Transport, Socket), + ConnArgs = [server, "localhost", Port, Socket, + {SslOpts, socket_options(SocketValues)}, self(), CbInfo], + case ssl_connection_sup:start_child(ConnArgs) of + {ok, Pid} -> + tls_connection:socket_control(Socket, Pid, Transport); + {error, Reason} -> + {error, Reason} + end; + {error, Reason} -> + {error, Reason} + end. + +%%-------------------------------------------------------------------- +-spec ssl_accept(#sslsocket{}) -> ok | {error, reason()}. +-spec ssl_accept(#sslsocket{} | port(), timeout()| [ssl_option() + | transport_option()]) -> + ok | {ok, #sslsocket{}} | {error, reason()}. +-spec ssl_accept(port(), [ssl_option()| transport_option()], timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. +%% +%% Description: Performs accept on an ssl listen socket. e.i. performs +%% ssl handshake. +%%-------------------------------------------------------------------- +ssl_accept(ListenSocket) -> + ssl_accept(ListenSocket, infinity). + +ssl_accept(#sslsocket{} = Socket, Timeout) -> + tls_connection:handshake(Socket, Timeout); + +ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) -> + ssl_accept(ListenSocket, SslOptions, infinity). + +ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> + {Transport,_,_,_} = + proplists:get_value(cb_info, SslOptions, {gen_tcp, tcp, tcp_closed, tcp_error}), + EmulatedOptions = emulated_options(), + {ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions), + try handle_options(SslOptions ++ SocketValues, server) of + {ok, #config{cb = CbInfo, ssl = SslOpts, emulated = EmOpts}} -> + ok = ssl_socket:setopts(Transport, Socket, internal_inet_values()), + {ok, Port} = ssl_socket:port(Transport, Socket), + tls_connection:ssl_accept(Port, Socket, + {SslOpts, EmOpts}, + self(), CbInfo, Timeout) + catch + Error = {error, _Reason} -> Error + end. + +%%-------------------------------------------------------------------- +-spec close(#sslsocket{}) -> term(). +%% +%% Description: Close an ssl connection +%%-------------------------------------------------------------------- +close(#sslsocket{pid = Pid}) when is_pid(Pid) -> + tls_connection:close(Pid); +close(#sslsocket{pid = {ListenSocket, #config{cb={Transport,_, _, _}}}}) -> + Transport:close(ListenSocket). + +%%-------------------------------------------------------------------- +-spec send(#sslsocket{}, iodata()) -> ok | {error, reason()}. +%% +%% Description: Sends data over the ssl connection +%%-------------------------------------------------------------------- +send(#sslsocket{pid = Pid}, Data) when is_pid(Pid) -> + tls_connection:send(Pid, Data); +send(#sslsocket{pid = {ListenSocket, #config{cb={Transport, _, _, _}}}}, Data) -> + Transport:send(ListenSocket, Data). %% {error,enotconn} + +%%-------------------------------------------------------------------- +-spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}. +-spec recv(#sslsocket{}, integer(), timeout()) -> {ok, binary()| list()} | {error, reason()}. +%% +%% Description: Receives data when active = false +%%-------------------------------------------------------------------- +recv(Socket, Length) -> + recv(Socket, Length, infinity). +recv(#sslsocket{pid = Pid}, Length, Timeout) when is_pid(Pid) -> + tls_connection:recv(Pid, Length, Timeout); +recv(#sslsocket{pid = {Listen, + #config{cb={Transport, _, _, _}}}}, _,_) when is_port(Listen)-> + Transport:recv(Listen, 0). %% {error,enotconn} + +%%-------------------------------------------------------------------- +-spec controlling_process(#sslsocket{}, pid()) -> ok | {error, reason()}. +%% +%% Description: Changes process that receives the messages when active = true +%% or once. +%%-------------------------------------------------------------------- +controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid), is_pid(NewOwner) -> + tls_connection:new_user(Pid, NewOwner); +controlling_process(#sslsocket{pid = {Listen, + #config{cb={Transport, _, _, _}}}}, + NewOwner) when is_port(Listen), + is_pid(NewOwner) -> + Transport:controlling_process(Listen, NewOwner). + +%%-------------------------------------------------------------------- +-spec connection_info(#sslsocket{}) -> {ok, {tls_atom_version(), erl_cipher_suite()}} | + {error, reason()}. +%% +%% Description: Returns ssl protocol and cipher used for the connection +%%-------------------------------------------------------------------- +connection_info(#sslsocket{pid = Pid}) when is_pid(Pid) -> + tls_connection:info(Pid); +connection_info(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> + {error, enotconn}. + +%%-------------------------------------------------------------------- +-spec peername(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}. +%% +%% Description: same as inet:peername/1. +%%-------------------------------------------------------------------- +peername(#sslsocket{pid = Pid, fd = {Transport, Socket}}) when is_pid(Pid)-> + ssl_socket:peername(Transport, Socket); +peername(#sslsocket{pid = {ListenSocket, #config{cb = {Transport,_,_,_}}}}) -> + ssl_socket:peername(Transport, ListenSocket). %% Will return {error, enotconn} + +%%-------------------------------------------------------------------- +-spec peercert(#sslsocket{}) ->{ok, DerCert::binary()} | {error, reason()}. +%% +%% Description: Returns the peercert. +%%-------------------------------------------------------------------- +peercert(#sslsocket{pid = Pid}) when is_pid(Pid) -> + case tls_connection:peer_certificate(Pid) of + {ok, undefined} -> + {error, no_peercert}; + Result -> + Result + end; +peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> + {error, enotconn}. + +%%-------------------------------------------------------------------- +-spec suite_definition(cipher_suite()) -> erl_cipher_suite(). +%% +%% Description: Return erlang cipher suite definition. +%%-------------------------------------------------------------------- +suite_definition(S) -> + {KeyExchange, Cipher, Hash, _} = ssl_cipher:suite_definition(S), + {KeyExchange, Cipher, Hash}. + +%%-------------------------------------------------------------------- +-spec negotiated_next_protocol(#sslsocket{}) -> {ok, binary()} | {error, reason()}. +%% +%% Description: Returns the next protocol that has been negotiated. If no +%% protocol has been negotiated will return {error, next_protocol_not_negotiated} +%%-------------------------------------------------------------------- +negotiated_next_protocol(#sslsocket{pid = Pid}) -> + tls_connection:negotiated_next_protocol(Pid). + +-spec cipher_suites() -> [erl_cipher_suite()]. +-spec cipher_suites(erlang | openssl | all) -> [erl_cipher_suite()] | [string()]. + +%% Description: Returns all supported cipher suites. +%%-------------------------------------------------------------------- +cipher_suites() -> + cipher_suites(erlang). + +cipher_suites(erlang) -> + Version = tls_record:highest_protocol_version([]), + [suite_definition(S) || S <- ssl_cipher:suites(Version)]; + +cipher_suites(openssl) -> + Version = tls_record:highest_protocol_version([]), + [ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)]; +cipher_suites(all) -> + Version = tls_record:highest_protocol_version([]), + Supported = ssl_cipher:suites(Version) + ++ ssl_cipher:anonymous_suites() + ++ ssl_cipher:psk_suites(Version) + ++ ssl_cipher:srp_suites(), + [suite_definition(S) || S <- Supported]. + +%%-------------------------------------------------------------------- +-spec getopts(#sslsocket{}, [gen_tcp:option_name()]) -> + {ok, [gen_tcp:option()]} | {error, reason()}. +%% +%% Description: Gets options +%%-------------------------------------------------------------------- +getopts(#sslsocket{pid = Pid}, OptionTags) when is_pid(Pid), is_list(OptionTags) -> + tls_connection:get_opts(Pid, OptionTags); +getopts(#sslsocket{pid = {ListenSocket, #config{cb = {Transport,_,_,_}}}}, + OptionTags) when is_list(OptionTags) -> + try ssl_socket:getopts(Transport, ListenSocket, OptionTags) of + {ok, _} = Result -> + Result; + {error, InetError} -> + {error, {options, {socket_options, OptionTags, InetError}}} + catch + _:_ -> + {error, {options, {socket_options, OptionTags}}} + end; +getopts(#sslsocket{}, OptionTags) -> + {error, {options, {socket_options, OptionTags}}}. + +%%-------------------------------------------------------------------- +-spec setopts(#sslsocket{}, [gen_tcp:option()]) -> ok | {error, reason()}. +%% +%% Description: Sets options +%%-------------------------------------------------------------------- +setopts(#sslsocket{pid = Pid}, Options0) when is_pid(Pid), is_list(Options0) -> + try proplists:expand([{binary, [{mode, binary}]}, + {list, [{mode, list}]}], Options0) of + Options -> + tls_connection:set_opts(Pid, Options) + catch + _:_ -> + {error, {options, {not_a_proplist, Options0}}} + end; + +setopts(#sslsocket{pid = {ListenSocket, #config{cb = {Transport,_,_,_}}}}, Options) when is_list(Options) -> + try ssl_socket:setopts(Transport, ListenSocket, Options) of + ok -> + ok; + {error, InetError} -> + {error, {options, {socket_options, Options, InetError}}} + catch + _:Error -> + {error, {options, {socket_options, Options, Error}}} + end; +setopts(#sslsocket{}, Options) -> + {error, {options,{not_a_proplist, Options}}}. + +%%--------------------------------------------------------------- +-spec shutdown(#sslsocket{}, read | write | read_write) -> ok | {error, reason()}. +%% +%% Description: Same as gen_tcp:shutdown/2 +%%-------------------------------------------------------------------- +shutdown(#sslsocket{pid = {Listen, #config{cb={Transport,_, _, _}}}}, + How) when is_port(Listen) -> + Transport:shutdown(Listen, How); +shutdown(#sslsocket{pid = Pid}, How) -> + tls_connection:shutdown(Pid, How). + +%%-------------------------------------------------------------------- +-spec sockname(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}. +%% +%% Description: Same as inet:sockname/1 +%%-------------------------------------------------------------------- +sockname(#sslsocket{pid = {Listen, #config{cb={Transport,_, _, _}}}}) when is_port(Listen) -> + ssl_socket:sockname(Transport, Listen); + +sockname(#sslsocket{pid = Pid, fd = {Transport, Socket}}) when is_pid(Pid) -> + ssl_socket:sockname(Transport, Socket). + +%%--------------------------------------------------------------- +-spec session_info(#sslsocket{}) -> {ok, list()} | {error, reason()}. +%% +%% Description: Returns list of session info currently [{session_id, session_id(), +%% {cipher_suite, cipher_suite()}] +%%-------------------------------------------------------------------- +session_info(#sslsocket{pid = Pid}) when is_pid(Pid) -> + tls_connection:session_info(Pid); +session_info(#sslsocket{pid = {Listen,_}}) when is_port(Listen) -> + {error, enotconn}. + +%%--------------------------------------------------------------- +-spec versions() -> [{ssl_app, string()} | {supported, [tls_atom_version()]} | + {available, [tls_atom_version()]}]. +%% +%% Description: Returns a list of relevant versions. +%%-------------------------------------------------------------------- +versions() -> + Vsns = tls_record:supported_protocol_versions(), + SupportedVsns = [tls_record:protocol_version(Vsn) || Vsn <- Vsns], + AvailableVsns = ?ALL_SUPPORTED_VERSIONS, + [{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}]. + + +%%--------------------------------------------------------------- +-spec renegotiate(#sslsocket{}) -> ok | {error, reason()}. +%% +%% Description: Initiates a renegotiation. +%%-------------------------------------------------------------------- +renegotiate(#sslsocket{pid = Pid}) when is_pid(Pid) -> + tls_connection:renegotiation(Pid); +renegotiate(#sslsocket{pid = {Listen,_}}) when is_port(Listen) -> + {error, enotconn}. + +%%-------------------------------------------------------------------- +-spec prf(#sslsocket{}, binary() | 'master_secret', binary(), + binary() | prf_random(), non_neg_integer()) -> + {ok, binary()} | {error, reason()}. +%% +%% Description: use a ssl sessions TLS PRF to generate key material +%%-------------------------------------------------------------------- +prf(#sslsocket{pid = Pid}, + Secret, Label, Seed, WantedLength) when is_pid(Pid) -> + tls_connection:prf(Pid, Secret, Label, Seed, WantedLength); +prf(#sslsocket{pid = {Listen,_}}, _,_,_,_) when is_port(Listen) -> + {error, enotconn}. + +%%-------------------------------------------------------------------- +-spec clear_pem_cache() -> ok. +%% +%% Description: Clear the PEM cache +%%-------------------------------------------------------------------- +clear_pem_cache() -> + ssl_manager:clear_pem_cache(). + +%%--------------------------------------------------------------- +-spec format_error({error, term()}) -> list(). +%% +%% Description: Creates error string. +%%-------------------------------------------------------------------- +format_error({error, Reason}) -> + format_error(Reason); +format_error(Reason) when is_list(Reason) -> + Reason; +format_error(closed) -> + "TLS connection is closed"; +format_error({tls_alert, Description}) -> + "TLS Alert: " ++ Description; +format_error({options,{FileType, File, Reason}}) when FileType == cacertfile; + FileType == certfile; + FileType == keyfile; + FileType == dhfile -> + Error = file_error_format(Reason), + file_desc(FileType) ++ File ++ ": " ++ Error; +format_error({options, {socket_options, Option, Error}}) -> + lists:flatten(io_lib:format("Invalid transport socket option ~p: ~s", [Option, format_error(Error)])); +format_error({options, {socket_options, Option}}) -> + lists:flatten(io_lib:format("Invalid socket option: ~p", [Option])); +format_error({options, Options}) -> + lists:flatten(io_lib:format("Invalid TLS option: ~p", [Options])); + +format_error(Error) -> + case inet:format_error(Error) of + "unknown POSIX" ++ _ -> + unexpected_format(Error); + Other -> + Other + end. + +%%-------------------------------------------------------------------- +-spec random_bytes(integer()) -> binary(). + +%% +%% Description: Generates cryptographically secure random sequence if possible +%% fallbacks on pseudo random function +%%-------------------------------------------------------------------- +random_bytes(N) -> + try crypto:strong_rand_bytes(N) of + RandBytes -> + RandBytes + catch + error:low_entropy -> + crypto:rand_bytes(N) + end. + +%%%-------------------------------------------------------------- +%%% Internal functions +%%%-------------------------------------------------------------------- +do_connect(Address, Port, + #config{cb=CbInfo, inet_user=UserOpts, ssl=SslOpts, + emulated=EmOpts,inet_ssl=SocketOpts}, + Timeout) -> + {Transport, _, _, _} = CbInfo, + try Transport:connect(Address, Port, SocketOpts, Timeout) of + {ok, Socket} -> + tls_connection:connect(Address, Port, Socket, {SslOpts,EmOpts}, + self(), CbInfo, Timeout); + {error, Reason} -> + {error, Reason} + catch + exit:{function_clause, _} -> + {error, {options, {cb_info, CbInfo}}}; + exit:badarg -> + {error, {options, {socket_options, UserOpts}}}; + exit:{badarg, _} -> + {error, {options, {socket_options, UserOpts}}} + end. + +handle_options(Opts0, _Role) -> + Opts = proplists:expand([{binary, [{mode, binary}]}, + {list, [{mode, list}]}], Opts0), + ReuseSessionFun = fun(_, _, _, _) -> true end, + + DefaultVerifyNoneFun = + {fun(_,{bad_cert, _}, UserState) -> + {valid, UserState}; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, UserState) -> + {valid, UserState}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, []}, + + VerifyNoneFun = handle_option(verify_fun, Opts, DefaultVerifyNoneFun), + + UserFailIfNoPeerCert = handle_option(fail_if_no_peer_cert, Opts, false), + UserVerifyFun = handle_option(verify_fun, Opts, undefined), + CaCerts = handle_option(cacerts, Opts, undefined), + + {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun} = + %% Handle 0, 1, 2 for backwards compatibility + case proplists:get_value(verify, Opts, verify_none) of + 0 -> + {verify_none, false, + ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun}; + 1 -> + {verify_peer, false, + ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun}; + 2 -> + {verify_peer, true, + ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun}; + verify_none -> + {verify_none, false, + ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun}; + verify_peer -> + {verify_peer, UserFailIfNoPeerCert, + ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun}; + Value -> + throw({error, {options, {verify, Value}}}) + end, + + CertFile = handle_option(certfile, Opts, <<>>), + + Versions = case handle_option(versions, Opts, []) of + [] -> + tls_record:supported_protocol_versions(); + Vsns -> + [tls_record:protocol_version(Vsn) || Vsn <- Vsns] + end, + + SSLOptions = #ssl_options{ + versions = Versions, + verify = validate_option(verify, Verify), + verify_fun = VerifyFun, + fail_if_no_peer_cert = FailIfNoPeerCert, + verify_client_once = handle_option(verify_client_once, Opts, false), + depth = handle_option(depth, Opts, 1), + cert = handle_option(cert, Opts, undefined), + certfile = CertFile, + key = handle_option(key, Opts, undefined), + keyfile = handle_option(keyfile, Opts, CertFile), + password = handle_option(password, Opts, ""), + cacerts = CaCerts, + cacertfile = handle_option(cacertfile, Opts, CaCertDefault), + dh = handle_option(dh, Opts, undefined), + dhfile = handle_option(dhfile, Opts, undefined), + user_lookup_fun = handle_option(user_lookup_fun, Opts, undefined), + psk_identity = handle_option(psk_identity, Opts, undefined), + srp_identity = handle_option(srp_identity, Opts, undefined), + ciphers = handle_option(ciphers, Opts, []), + %% Server side option + reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun), + reuse_sessions = handle_option(reuse_sessions, Opts, true), + secure_renegotiate = handle_option(secure_renegotiate, Opts, false), + renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT), + hibernate_after = handle_option(hibernate_after, Opts, undefined), + erl_dist = handle_option(erl_dist, Opts, false), + next_protocols_advertised = + handle_option(next_protocols_advertised, Opts, undefined), + next_protocol_selector = + make_next_protocol_selector( + handle_option(client_preferred_next_protocols, Opts, undefined)), + log_alert = handle_option(log_alert, Opts, true) + }, + + CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}), + SslOptions = [versions, verify, verify_fun, + fail_if_no_peer_cert, verify_client_once, + depth, cert, certfile, key, keyfile, + password, cacerts, cacertfile, dh, dhfile, + user_lookup_fun, psk_identity, srp_identity, ciphers, + reuse_session, reuse_sessions, ssl_imp, + cb_info, renegotiate_at, secure_renegotiate, hibernate_after, + erl_dist, next_protocols_advertised, + client_preferred_next_protocols, log_alert], + + SockOpts = lists:foldl(fun(Key, PropList) -> + proplists:delete(Key, PropList) + end, Opts, SslOptions), + + {SSLsock, Emulated} = emulated_options(SockOpts), + {ok, #config{ssl=SSLOptions, emulated=Emulated, inet_ssl=SSLsock, + inet_user=SockOpts, cb=CbInfo}}. + +handle_option(OptionName, Opts, Default) -> + validate_option(OptionName, + proplists:get_value(OptionName, Opts, Default)). + + +validate_option(versions, Versions) -> + validate_versions(Versions, Versions); +validate_option(verify, Value) + when Value == verify_none; Value == verify_peer -> + Value; +validate_option(verify_fun, undefined) -> + undefined; +%% Backwards compatibility +validate_option(verify_fun, Fun) when is_function(Fun) -> + {fun(_,{bad_cert, _} = Reason, OldFun) -> + case OldFun([Reason]) of + true -> + {valid, OldFun}; + false -> + {fail, Reason} + end; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, UserState) -> + {valid, UserState}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, Fun}; +validate_option(verify_fun, {Fun, _} = Value) when is_function(Fun) -> + Value; +validate_option(fail_if_no_peer_cert, Value) + when Value == true; Value == false -> + Value; +validate_option(verify_client_once, Value) + when Value == true; Value == false -> + Value; +validate_option(depth, Value) when is_integer(Value), + Value >= 0, Value =< 255-> + Value; +validate_option(cert, Value) when Value == undefined; + is_binary(Value) -> + Value; +validate_option(certfile, undefined = Value) -> + Value; +validate_option(certfile, Value) when is_binary(Value) -> + Value; +validate_option(certfile, Value) when is_list(Value) -> + list_to_binary(Value); + +validate_option(key, undefined) -> + undefined; +validate_option(key, {KeyType, Value}) when is_binary(Value), + KeyType == rsa; %% Backwards compatibility + KeyType == dsa; %% Backwards compatibility + KeyType == 'RSAPrivateKey'; + KeyType == 'DSAPrivateKey'; + KeyType == 'PrivateKeyInfo' -> + {KeyType, Value}; + +validate_option(keyfile, undefined) -> + <<>>; +validate_option(keyfile, Value) when is_binary(Value) -> + Value; +validate_option(keyfile, Value) when is_list(Value), Value =/= "" -> + list_to_binary(Value); +validate_option(password, Value) when is_list(Value) -> + Value; + +validate_option(cacerts, Value) when Value == undefined; + is_list(Value) -> + Value; +%% certfile must be present in some cases otherwhise it can be set +%% to the empty string. +validate_option(cacertfile, undefined) -> + <<>>; +validate_option(cacertfile, Value) when is_binary(Value) -> + Value; +validate_option(cacertfile, Value) when is_list(Value), Value =/= ""-> + list_to_binary(Value); +validate_option(dh, Value) when Value == undefined; + is_binary(Value) -> + Value; +validate_option(dhfile, undefined = Value) -> + Value; +validate_option(dhfile, Value) when is_binary(Value) -> + Value; +validate_option(dhfile, Value) when is_list(Value), Value =/= "" -> + list_to_binary(Value); +validate_option(psk_identity, undefined) -> + undefined; +validate_option(psk_identity, Identity) + when is_list(Identity), Identity =/= "", length(Identity) =< 65535 -> + list_to_binary(Identity); +validate_option(user_lookup_fun, undefined) -> + undefined; +validate_option(user_lookup_fun, {Fun, _} = Value) when is_function(Fun, 3) -> + Value; +validate_option(srp_identity, undefined) -> + undefined; +validate_option(srp_identity, {Username, Password}) + when is_list(Username), is_list(Password), Username =/= "", length(Username) =< 255 -> + {list_to_binary(Username), list_to_binary(Password)}; + +validate_option(ciphers, Value) when is_list(Value) -> + Version = tls_record:highest_protocol_version([]), + try cipher_suites(Version, Value) + catch + exit:_ -> + throw({error, {options, {ciphers, Value}}}); + error:_-> + throw({error, {options, {ciphers, Value}}}) + end; +validate_option(reuse_session, Value) when is_function(Value) -> + Value; +validate_option(reuse_sessions, Value) when Value == true; + Value == false -> + Value; + +validate_option(secure_renegotiate, Value) when Value == true; + Value == false -> + Value; +validate_option(renegotiate_at, Value) when is_integer(Value) -> + erlang:min(Value, ?DEFAULT_RENEGOTIATE_AT); + +validate_option(hibernate_after, undefined) -> + undefined; +validate_option(hibernate_after, Value) when is_integer(Value), Value >= 0 -> + Value; +validate_option(erl_dist,Value) when Value == true; + Value == false -> + Value; +validate_option(client_preferred_next_protocols = Opt, {Precedence, PreferredProtocols} = Value) + when is_list(PreferredProtocols) -> + case tls_record:highest_protocol_version([]) of + {3,0} -> + throw({error, {options, {not_supported_in_sslv3, {Opt, Value}}}}); + _ -> + validate_binary_list(client_preferred_next_protocols, PreferredProtocols), + validate_npn_ordering(Precedence), + {Precedence, PreferredProtocols, ?NO_PROTOCOL} + end; +validate_option(client_preferred_next_protocols = Opt, {Precedence, PreferredProtocols, Default} = Value) + when is_list(PreferredProtocols), is_binary(Default), + byte_size(Default) > 0, byte_size(Default) < 256 -> + case tls_record:highest_protocol_version([]) of + {3,0} -> + throw({error, {options, {not_supported_in_sslv3, {Opt, Value}}}}); + _ -> + validate_binary_list(client_preferred_next_protocols, PreferredProtocols), + validate_npn_ordering(Precedence), + Value + end; + +validate_option(client_preferred_next_protocols, undefined) -> + undefined; +validate_option(log_alert, Value) when Value == true; + Value == false -> + Value; +validate_option(next_protocols_advertised = Opt, Value) when is_list(Value) -> + case tls_record:highest_protocol_version([]) of + {3,0} -> + throw({error, {options, {not_supported_in_sslv3, {Opt, Value}}}}); + _ -> + validate_binary_list(next_protocols_advertised, Value), + Value + end; + +validate_option(next_protocols_advertised, undefined) -> + undefined; +validate_option(Opt, Value) -> + throw({error, {options, {Opt, Value}}}). + +validate_npn_ordering(client) -> + ok; +validate_npn_ordering(server) -> + ok; +validate_npn_ordering(Value) -> + throw({error, {options, {client_preferred_next_protocols, {invalid_precedence, Value}}}}). + +validate_binary_list(Opt, List) -> + lists:foreach( + fun(Bin) when is_binary(Bin), + byte_size(Bin) > 0, + byte_size(Bin) < 256 -> + ok; + (Bin) -> + throw({error, {options, {Opt, {invalid_protocol, Bin}}}}) + end, List). + +validate_versions([], Versions) -> + Versions; +validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2'; + Version == 'tlsv1.1'; + Version == tlsv1; + Version == sslv3 -> + validate_versions(Rest, Versions); +validate_versions([Ver| _], Versions) -> + throw({error, {options, {Ver, {versions, Versions}}}}). + +validate_inet_option(mode, Value) + when Value =/= list, Value =/= binary -> + throw({error, {options, {mode,Value}}}); +validate_inet_option(packet, Value) + when not (is_atom(Value) orelse is_integer(Value)) -> + throw({error, {options, {packet,Value}}}); +validate_inet_option(packet_size, Value) + when not is_integer(Value) -> + throw({error, {options, {packet_size,Value}}}); +validate_inet_option(header, Value) + when not is_integer(Value) -> + throw({error, {options, {header,Value}}}); +validate_inet_option(active, Value) + when Value =/= true, Value =/= false, Value =/= once -> + throw({error, {options, {active,Value}}}); +validate_inet_option(_, _) -> + ok. + +%% The option cacerts overrides cacertsfile +ca_cert_default(_,_, [_|_]) -> + undefined; +ca_cert_default(verify_none, _, _) -> + undefined; +ca_cert_default(verify_peer, {Fun,_}, _) when is_function(Fun) -> + undefined; +%% Server that wants to verify_peer and has no verify_fun must have +%% some trusted certs. +ca_cert_default(verify_peer, undefined, _) -> + "". + +emulated_options() -> + [mode, packet, active, header, packet_size]. + +internal_inet_values() -> + [{packet_size,0},{packet, 0},{header, 0},{active, false},{mode,binary}]. + +socket_options(InetValues) -> + #socket_options{ + mode = proplists:get_value(mode, InetValues, lists), + header = proplists:get_value(header, InetValues, 0), + active = proplists:get_value(active, InetValues, active), + packet = proplists:get_value(packet, InetValues, 0), + packet_size = proplists:get_value(packet_size, InetValues) + }. + +emulated_options(Opts) -> + emulated_options(Opts, internal_inet_values(), #socket_options{}). + +emulated_options([{mode,Opt}|Opts], Inet, Emulated) -> + validate_inet_option(mode,Opt), + emulated_options(Opts, Inet, Emulated#socket_options{mode=Opt}); +emulated_options([{header,Opt}|Opts], Inet, Emulated) -> + validate_inet_option(header,Opt), + emulated_options(Opts, Inet, Emulated#socket_options{header=Opt}); +emulated_options([{active,Opt}|Opts], Inet, Emulated) -> + validate_inet_option(active,Opt), + emulated_options(Opts, Inet, Emulated#socket_options{active=Opt}); +emulated_options([{packet,Opt}|Opts], Inet, Emulated) -> + validate_inet_option(packet,Opt), + emulated_options(Opts, Inet, Emulated#socket_options{packet=Opt}); +emulated_options([{packet_size,Opt}|Opts], Inet, Emulated) -> + validate_inet_option(packet_size,Opt), + emulated_options(Opts, Inet, Emulated#socket_options{packet_size=Opt}); +emulated_options([Opt|Opts], Inet, Emulated) -> + emulated_options(Opts, [Opt|Inet], Emulated); +emulated_options([], Inet,Emulated) -> + {Inet, Emulated}. + +cipher_suites(Version, []) -> + ssl_cipher:suites(Version); +cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %% Backwards compatibility + Ciphers = [{KeyExchange, Cipher, Hash} || {KeyExchange, Cipher, Hash, _} <- Ciphers0], + cipher_suites(Version, Ciphers); +cipher_suites(Version, [{_,_,_}| _] = Ciphers0) -> + Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0], + cipher_suites(Version, Ciphers); + +cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) -> + Supported0 = ssl_cipher:suites(Version) + ++ ssl_cipher:anonymous_suites() + ++ ssl_cipher:psk_suites(Version) + ++ ssl_cipher:srp_suites(), + Supported = ssl_cipher:filter_suites(Supported0), + case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported)] of + [] -> + Supported; + Ciphers -> + Ciphers + end; +cipher_suites(Version, [Head | _] = Ciphers0) when is_list(Head) -> + %% Format: ["RC4-SHA","RC4-MD5"] + Ciphers = [ssl_cipher:openssl_suite(C) || C <- Ciphers0], + cipher_suites(Version, Ciphers); +cipher_suites(Version, Ciphers0) -> + %% Format: "RC4-SHA:RC4-MD5" + Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:tokens(Ciphers0, ":")], + cipher_suites(Version, Ciphers). + +unexpected_format(Error) -> + lists:flatten(io_lib:format("Unexpected error: ~p", [Error])). + +file_error_format({error, Error})-> + case file:format_error(Error) of + "unknown POSIX error" -> + "decoding error"; + Str -> + Str + end; +file_error_format(_) -> + "decoding error". + +file_desc(cacertfile) -> + "Invalid CA certificate file "; +file_desc(certfile) -> + "Invalid certificate file "; +file_desc(keyfile) -> + "Invalid key file "; +file_desc(dhfile) -> + "Invalid DH params file ". + +detect(_Pred, []) -> + undefined; +detect(Pred, [H|T]) -> + case Pred(H) of + true -> + H; + _ -> + detect(Pred, T) + end. + +make_next_protocol_selector(undefined) -> + undefined; +make_next_protocol_selector({client, AllProtocols, DefaultProtocol}) -> + fun(AdvertisedProtocols) -> + case detect(fun(PreferredProtocol) -> + lists:member(PreferredProtocol, AdvertisedProtocols) + end, AllProtocols) of + undefined -> + DefaultProtocol; + PreferredProtocol -> + PreferredProtocol + end + end; + +make_next_protocol_selector({server, AllProtocols, DefaultProtocol}) -> + fun(AdvertisedProtocols) -> + case detect(fun(PreferredProtocol) -> + lists:member(PreferredProtocol, AllProtocols) + end, + AdvertisedProtocols) of + undefined -> + DefaultProtocol; + PreferredProtocol -> + PreferredProtocol + end + end. diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/tls_connection.erl index 73857bccbb..d56864b384 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -25,23 +25,23 @@ %% sent according to the SSL-record protocol. %%---------------------------------------------------------------------- --module(ssl_connection). +-module(tls_connection). -behaviour(gen_fsm). --include("ssl_handshake.hrl"). +-include("tls_handshake.hrl"). -include("ssl_alert.hrl"). --include("ssl_record.hrl"). +-include("tls_record.hrl"). -include("ssl_cipher.hrl"). -include("ssl_internal.hrl"). +-include("ssl_srp.hrl"). -include_lib("public_key/include/public_key.hrl"). %% Internal application API -export([send/2, recv/3, connect/7, ssl_accept/6, handshake/2, socket_control/3, close/1, shutdown/2, new_user/2, get_opts/2, set_opts/2, info/1, session_info/1, - peer_certificate/1, sockname/1, peername/1, renegotiation/1, - prf/5]). + peer_certificate/1, renegotiation/1, negotiated_next_protocol/1, prf/5]). %% Called by ssl_connection_sup -export([start_link/7]). @@ -70,11 +70,10 @@ tls_handshake_history, % tls_handshake_history() tls_cipher_texts, % list() received but not deciphered yet cert_db, % - session, % #session{} from ssl_handshake.hrl + session, % #session{} from tls_handshake.hrl session_cache, % session_cache_cb, % negotiated_version, % tls_version() - supported_protocol_versions, % [atom()] client_certificate_requested = false, key_algorithm, % atom as defined by cipher_suite hashsign_algorithm, % atom as defined by cipher_suite @@ -82,6 +81,9 @@ private_key, % PKIX: #'RSAPrivateKey'{} diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side diffie_hellman_keys, % {PublicKey, PrivateKey} + psk_identity, % binary() - server psk identity hint + srp_params, % #srp_user{} + srp_keys, % {PublicKey, PrivateKey} premaster_secret, % file_ref_db, % ets() cert_db_ref, % ref() @@ -92,7 +94,10 @@ timer, % start_or_recv_timer send_queue, % queue() terminated = false, % - allow_renegotiate = true + allow_renegotiate = true, + expecting_next_protocol_negotiation = false :: boolean(), + next_protocol = undefined :: undefined | binary(), + client_ecc % {Curves, PointFmt} }). -define(DEFAULT_DIFFIE_HELLMAN_PARAMS, @@ -176,10 +181,10 @@ handshake(#sslsocket{pid = Pid}, Timeout) -> %% %% Description: Set the ssl process to own the accept socket %%-------------------------------------------------------------------- -socket_control(Socket, Pid, CbModule) -> - case CbModule:controlling_process(Socket, Pid) of +socket_control(Socket, Pid, Transport) -> + case Transport:controlling_process(Socket, Pid) of ok -> - {ok, sslsocket(Pid)}; + {ok, ssl_socket:socket(Pid, Transport, Socket)}; {error, Reason} -> {error, Reason} end. @@ -213,20 +218,15 @@ shutdown(ConnectionPid, How) -> %%-------------------------------------------------------------------- new_user(ConnectionPid, User) -> sync_send_all_state_event(ConnectionPid, {new_user, User}). + %%-------------------------------------------------------------------- --spec sockname(pid()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}. -%% -%% Description: Same as inet:sockname/1 -%%-------------------------------------------------------------------- -sockname(ConnectionPid) -> - sync_send_all_state_event(ConnectionPid, sockname). -%%-------------------------------------------------------------------- --spec peername(pid()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}. +-spec negotiated_next_protocol(pid()) -> {ok, binary()} | {error, reason()}. %% -%% Description: Same as inet:peername/1 +%% Description: Returns the negotiated protocol %%-------------------------------------------------------------------- -peername(ConnectionPid) -> - sync_send_all_state_event(ConnectionPid, peername). +negotiated_next_protocol(ConnectionPid) -> + sync_send_all_state_event(ConnectionPid, negotiated_next_protocol). + %%-------------------------------------------------------------------- -spec get_opts(pid(), list()) -> {ok, list()} | {error, reason()}. %% @@ -301,7 +301,7 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) -> init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo), - Handshake = ssl_handshake:init_handshake_history(), + Handshake = tls_handshake:init_handshake_history(), TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), try ssl_init(SSLOpts0, Role) of {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} -> @@ -341,11 +341,11 @@ hello(start, #state{host = Host, port = Port, role = client, transport_cb = Transport, socket = Socket, connection_states = ConnectionStates0, renegotiation = {Renegotiation, _}} = State0) -> - Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, + Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Cache, CacheCb, Renegotiation, Cert), Version = Hello#client_hello.client_version, - Handshake0 = ssl_handshake:init_handshake_history(), + Handshake0 = tls_handshake:init_handshake_history(), {BinMsg, ConnectionStates, Handshake} = encode_handshake(Hello, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), @@ -373,17 +373,29 @@ hello(#server_hello{cipher_suite = CipherSuite, negotiated_version = ReqVersion, renegotiation = {Renegotiation, _}, ssl_options = SslOptions} = State0) -> - case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of - {Version, NewId, ConnectionStates} -> + case tls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of + #alert{} = Alert -> + handle_own_alert(Alert, ReqVersion, hello, State0); + {Version, NewId, ConnectionStates, NextProtocol} -> {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite), - + PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), + + NewNextProtocol = case NextProtocol of + undefined -> + State0#state.next_protocol; + _ -> + NextProtocol + end, + State = State0#state{key_algorithm = KeyAlgorithm, hashsign_algorithm = default_hashsign(Version, KeyAlgorithm), negotiated_version = Version, connection_states = ConnectionStates, - premaster_secret = PremasterSecret}, + premaster_secret = PremasterSecret, + expecting_next_protocol_negotiation = NextProtocol =/= undefined, + next_protocol = NewNextProtocol}, case ssl_session:is_new(OldId, NewId) of true -> @@ -391,26 +403,26 @@ hello(#server_hello{cipher_suite = CipherSuite, State#state{connection_states = ConnectionStates}); false -> handle_resumed_session(NewId, State#state{connection_states = ConnectionStates}) - end; - #alert{} = Alert -> - handle_own_alert(Alert, ReqVersion, hello, State0), - {stop, {shutdown, own_alert}, State0} + end end; -hello(Hello = #client_hello{client_version = ClientVersion}, +hello(Hello = #client_hello{client_version = ClientVersion}, State = #state{connection_states = ConnectionStates0, port = Port, session = #session{own_certificate = Cert} = Session0, renegotiation = {Renegotiation, _}, session_cache = Cache, session_cache_cb = CacheCb, ssl_options = SslOpts}) -> - case ssl_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, + case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) of - {Version, {Type, Session}, ConnectionStates} -> - do_server_hello(Type, State#state{connection_states = - ConnectionStates, - negotiated_version = Version, - session = Session}); + {Version, {Type, Session}, ConnectionStates, ProtocolsToAdvertise, + EcPointFormats, EllipticCurves} -> + do_server_hello(Type, ProtocolsToAdvertise, + EcPointFormats, EllipticCurves, + State#state{connection_states = ConnectionStates, + negotiated_version = Version, + session = Session, + client_ecc = {EllipticCurves, EcPointFormats}}); #alert{} = Alert -> handle_own_alert(Alert, ClientVersion, hello, State) end; @@ -435,11 +447,11 @@ abbreviated(#finished{verify_data = Data} = Finished, session = #session{master_secret = MasterSecret}, connection_states = ConnectionStates0} = State) -> - case ssl_handshake:verify_connection(Version, Finished, client, + case tls_handshake:verify_connection(Version, Finished, client, get_current_connection_state_prf(ConnectionStates0, write), MasterSecret, Handshake) of verified -> - ConnectionStates = ssl_record:set_client_verify_data(current_both, Data, ConnectionStates0), + ConnectionStates = tls_record:set_client_verify_data(current_both, Data, ConnectionStates0), next_state_connection(abbreviated, ack_connection(State#state{connection_states = ConnectionStates})); #alert{} = Alert -> @@ -451,11 +463,11 @@ abbreviated(#finished{verify_data = Data} = Finished, session = #session{master_secret = MasterSecret}, negotiated_version = Version, connection_states = ConnectionStates0} = State) -> - case ssl_handshake:verify_connection(Version, Finished, server, + case tls_handshake:verify_connection(Version, Finished, server, get_pending_connection_state_prf(ConnectionStates0, write), MasterSecret, Handshake0) of verified -> - ConnectionStates1 = ssl_record:set_server_verify_data(current_read, Data, ConnectionStates0), + ConnectionStates1 = tls_record:set_server_verify_data(current_read, Data, ConnectionStates0), {ConnectionStates, Handshake} = finalize_handshake(State#state{connection_states = ConnectionStates1}, abbreviated), next_state_connection(abbreviated, @@ -466,6 +478,13 @@ abbreviated(#finished{verify_data = Data} = Finished, handle_own_alert(Alert, Version, abbreviated, State) end; +%% only allowed to send next_protocol message after change cipher spec +%% & before finished message and it is not allowed during renegotiation +abbreviated(#next_protocol{selected_protocol = SelectedProtocol}, + #state{role = server, expecting_next_protocol_negotiation = true} = State0) -> + {Record, State} = next_record(State0#state{next_protocol = SelectedProtocol}), + next_state(abbreviated, abbreviated, Record, State); + abbreviated(timeout, State) -> { next_state, abbreviated, State, hibernate }; @@ -503,7 +522,7 @@ certify(#certificate{} = Cert, cert_db = CertDbHandle, cert_db_ref = CertDbRef, ssl_options = Opts} = State) -> - case ssl_handshake:certify(Cert, CertDbHandle, CertDbRef, Opts#ssl_options.depth, + case tls_handshake:certify(Cert, CertDbHandle, CertDbRef, Opts#ssl_options.depth, Opts#ssl_options.verify, Opts#ssl_options.verify_fun, Role) of {PeerCert, PublicKeyInfo} -> @@ -516,7 +535,11 @@ certify(#certificate{} = Cert, certify(#server_key_exchange{} = KeyExchangeMsg, #state{role = client, negotiated_version = Version, key_algorithm = Alg} = State0) - when Alg == dhe_dss; Alg == dhe_rsa; Alg == dh_anon -> + when Alg == dhe_dss; Alg == dhe_rsa; + Alg == ecdhe_rsa; Alg == ecdhe_ecdsa; + Alg == dh_anon; Alg == ecdh_anon; + Alg == psk; Alg == dhe_psk; Alg == rsa_psk; + Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon -> case handle_server_key(KeyExchangeMsg, State0) of #state{} = State1 -> {Record, State} = next_record(State1), @@ -533,6 +556,45 @@ certify(#certificate_request{}, State0) -> {Record, State} = next_record(State0#state{client_certificate_requested = true}), next_state(certify, certify, Record, State); +%% PSK and RSA_PSK might bypass the Server-Key-Exchange +certify(#server_hello_done{}, + #state{session = #session{master_secret = undefined}, + negotiated_version = Version, + psk_identity = PSKIdentity, + premaster_secret = undefined, + role = client, + key_algorithm = Alg} = State0) + when Alg == psk -> + case server_psk_master_secret(PSKIdentity, State0) of + #state{} = State -> + client_certify_and_key_exchange(State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, certify, State0) + end; + +certify(#server_hello_done{}, + #state{session = #session{master_secret = undefined}, + ssl_options = SslOpts, + negotiated_version = Version, + psk_identity = PSKIdentity, + premaster_secret = undefined, + role = client, + key_algorithm = Alg} = State0) + when Alg == rsa_psk -> + case handle_psk_identity(PSKIdentity, SslOpts#ssl_options.user_lookup_fun) of + {ok, PSK} when is_binary(PSK) -> + PremasterSecret = make_premaster_secret(Version, rsa), + Len = byte_size(PSK), + RealPMS = <<?UINT16(48), PremasterSecret/binary, ?UINT16(Len), PSK/binary>>, + State1 = State0#state{premaster_secret = PremasterSecret}, + State = master_from_premaster_secret(RealPMS, State1), + client_certify_and_key_exchange(State); + #alert{} = Alert -> + Alert; + _ -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + end; + %% Master secret was determined with help of server-key exchange msg certify(#server_hello_done{}, #state{session = #session{master_secret = MasterSecret} = Session, @@ -540,7 +602,7 @@ certify(#server_hello_done{}, negotiated_version = Version, premaster_secret = undefined, role = client} = State0) -> - case ssl_handshake:master_secret(Version, Session, + case tls_handshake:master_secret(Version, Session, ConnectionStates0, client) of {MasterSecret, ConnectionStates} -> State = State0#state{connection_states = ConnectionStates}, @@ -556,7 +618,7 @@ certify(#server_hello_done{}, negotiated_version = Version, premaster_secret = PremasterSecret, role = client} = State0) -> - case ssl_handshake:master_secret(Version, PremasterSecret, + case tls_handshake:master_secret(Version, PremasterSecret, ConnectionStates0, client) of {MasterSecret, ConnectionStates} -> Session = Session0#session{master_secret = MasterSecret}, @@ -577,12 +639,13 @@ certify(#client_key_exchange{} = Msg, certify(#client_key_exchange{exchange_keys = Keys}, State = #state{key_algorithm = KeyAlg, negotiated_version = Version}) -> try - certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, Version), State) + certify_client_key_exchange(tls_handshake:decode_client_key(Keys, KeyAlg, Version), State) catch #alert{} = Alert -> handle_own_alert(Alert, Version, certify, State) end; + certify(timeout, State) -> { next_state, certify, State, hibernate }; @@ -594,8 +657,8 @@ certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS connection_states = ConnectionStates0, session = Session0, private_key = Key} = State0) -> - PremasterSecret = ssl_handshake:decrypt_premaster_secret(EncPMS, Key), - case ssl_handshake:master_secret(Version, PremasterSecret, + PremasterSecret = tls_handshake:decrypt_premaster_secret(EncPMS, Key), + case tls_handshake:master_secret(Version, PremasterSecret, ConnectionStates0, server) of {MasterSecret, ConnectionStates} -> Session = Session0#session{master_secret = MasterSecret}, @@ -609,10 +672,74 @@ certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey}, #state{negotiated_version = Version, + diffie_hellman_params = #'DHParameter'{} = Params, + diffie_hellman_keys = {_, ServerDhPrivateKey}} = State0) -> + case dh_master_secret(Params, ClientPublicDhKey, ServerDhPrivateKey, State0) of + #state{} = State1 -> + {Record, State} = next_record(State1), + next_state(certify, cipher, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, certify, State0) + end; + +certify_client_key_exchange(#client_ec_diffie_hellman_public{dh_public = ClientPublicEcDhPoint}, + #state{negotiated_version = Version, + diffie_hellman_keys = ECDHKey} = State0) -> + case ec_dh_master_secret(ECDHKey, #'ECPoint'{point = ClientPublicEcDhPoint}, State0) of + #state{} = State1 -> + {Record, State} = next_record(State1), + next_state(certify, cipher, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, certify, State0) + end; + +certify_client_key_exchange(#client_psk_identity{identity = ClientPSKIdentity}, + #state{negotiated_version = Version} = State0) -> + case server_psk_master_secret(ClientPSKIdentity, State0) of + #state{} = State1 -> + {Record, State} = next_record(State1), + next_state(certify, cipher, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, certify, State0) + end; + +certify_client_key_exchange(#client_dhe_psk_identity{ + identity = ClientPSKIdentity, + dh_public = ClientPublicDhKey}, + #state{negotiated_version = Version, diffie_hellman_params = #'DHParameter'{prime = P, base = G}, diffie_hellman_keys = {_, ServerDhPrivateKey}} = State0) -> - case dh_master_secret(crypto:mpint(P), crypto:mpint(G), ClientPublicDhKey, ServerDhPrivateKey, State0) of + case dhe_psk_master_secret(ClientPSKIdentity, P, G, ClientPublicDhKey, ServerDhPrivateKey, State0) of + #state{} = State1 -> + {Record, State} = next_record(State1), + next_state(certify, cipher, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, certify, State0) + end; + +certify_client_key_exchange(#client_rsa_psk_identity{ + identity = PskIdentity, + exchange_keys = + #encrypted_premaster_secret{premaster_secret= EncPMS}}, + #state{negotiated_version = Version, + private_key = Key} = State0) -> + PremasterSecret = tls_handshake:decrypt_premaster_secret(EncPMS, Key), + case server_rsa_psk_master_secret(PskIdentity, PremasterSecret, State0) of + #state{} = State1 -> + {Record, State} = next_record(State1), + next_state(certify, cipher, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, certify, State0) + end; + +certify_client_key_exchange(#client_srp_public{srp_a = ClientPublicKey}, + #state{negotiated_version = Version, + srp_params = + #srp_user{prime = Prime, + verifier = Verifier} + } = State0) -> + case server_srp_master_secret(Verifier, Prime, ClientPublicKey, State0) of #state{} = State1 -> {Record, State} = next_record(State1), next_state(certify, cipher, Record, State); @@ -640,7 +767,7 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS {_, _} -> CertHashSign; _ -> ConnectionHashSign end, - case ssl_handshake:certificate_verify(Signature, PublicKeyInfo, + case tls_handshake:certificate_verify(Signature, PublicKeyInfo, Version, HashSign, MasterSecret, Handshake) of valid -> {Record, State} = next_record(State0), @@ -649,6 +776,11 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS handle_own_alert(Alert, Version, cipher, State0) end; +%% client must send a next protocol message if we are expecting it +cipher(#finished{}, #state{role = server, expecting_next_protocol_negotiation = true, + next_protocol = undefined, negotiated_version = Version} = State0) -> + handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0); + cipher(#finished{verify_data = Data} = Finished, #state{negotiated_version = Version, host = Host, @@ -658,7 +790,7 @@ cipher(#finished{verify_data = Data} = Finished, = Session0, connection_states = ConnectionStates0, tls_handshake_history = Handshake0} = State) -> - case ssl_handshake:verify_connection(Version, Finished, + case tls_handshake:verify_connection(Version, Finished, opposite_role(Role), get_current_connection_state_prf(ConnectionStates0, read), MasterSecret, Handshake0) of @@ -669,6 +801,13 @@ cipher(#finished{verify_data = Data} = Finished, handle_own_alert(Alert, Version, cipher, State) end; +%% only allowed to send next_protocol message after change cipher spec +%% & before finished message and it is not allowed during renegotiation +cipher(#next_protocol{selected_protocol = SelectedProtocol}, + #state{role = server, expecting_next_protocol_negotiation = true} = State0) -> + {Record, State} = next_record(State0#state{next_protocol = SelectedProtocol}), + next_state(cipher, cipher, Record, State); + cipher(timeout, State) -> { next_state, cipher, State, hibernate }; @@ -689,7 +828,7 @@ connection(#hello_request{}, #state{host = Host, port = Port, connection_states = ConnectionStates0, renegotiation = {Renegotiation, _}, tls_handshake_history = Handshake0} = State0) -> - Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, + Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Cache, CacheCb, Renegotiation, Cert), {BinMsg, ConnectionStates, Handshake} = @@ -828,25 +967,22 @@ handle_sync_event({new_user, User}, _From, StateName, handle_sync_event({get_opts, OptTags}, _From, StateName, #state{socket = Socket, + transport_cb = Transport, socket_options = SockOpts} = State) -> - OptsReply = get_socket_opts(Socket, OptTags, SockOpts, []), + OptsReply = get_socket_opts(Transport, Socket, OptTags, SockOpts, []), {reply, OptsReply, StateName, State, get_timeout(State)}; -handle_sync_event(sockname, _From, StateName, - #state{socket = Socket} = State) -> - SockNameReply = inet:sockname(Socket), - {reply, SockNameReply, StateName, State, get_timeout(State)}; - -handle_sync_event(peername, _From, StateName, - #state{socket = Socket} = State) -> - PeerNameReply = inet:peername(Socket), - {reply, PeerNameReply, StateName, State, get_timeout(State)}; +handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = undefined} = State) -> + {reply, {error, next_protocol_not_negotiated}, StateName, State, get_timeout(State)}; +handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = NextProtocol} = State) -> + {reply, {ok, NextProtocol}, StateName, State, get_timeout(State)}; handle_sync_event({set_opts, Opts0}, _From, StateName, #state{socket_options = Opts1, socket = Socket, + transport_cb = Transport, user_data_buffer = Buffer} = State0) -> - {Reply, Opts} = set_socket_opts(Socket, Opts0, Opts1, []), + {Reply, Opts} = set_socket_opts(Transport, Socket, Opts0, Opts1, []), State1 = State0#state{socket_options = Opts}, if Opts#socket_options.active =:= false -> @@ -887,7 +1023,7 @@ handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName, #state{connection_states = ConnectionStates, negotiated_version = Version} = State) -> ConnectionState = - ssl_record:current_connection_state(ConnectionStates, read), + tls_record:current_connection_state(ConnectionStates, read), SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{master_secret = MasterSecret, client_random = ClientRandom, @@ -902,7 +1038,7 @@ handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName, (client_random, Acc) -> [ClientRandom|Acc]; (server_random, Acc) -> [ServerRandom|Acc] end, [], Seed)), - ssl_handshake:prf(Version, SecretToUse, Label, SeedToUse, WantedLength) + tls_handshake:prf(Version, SecretToUse, Label, SeedToUse, WantedLength) catch exit:_ -> {error, badarg}; error:Reason -> {error, Reason} @@ -913,7 +1049,7 @@ handle_sync_event(info, _, StateName, #state{negotiated_version = Version, session = #session{cipher_suite = Suite}} = State) -> - AtomVersion = ssl_record:protocol_version(Version), + AtomVersion = tls_record:protocol_version(Version), {reply, {ok, {AtomVersion, ssl:suite_definition(Suite)}}, StateName, State, get_timeout(State)}; @@ -967,9 +1103,10 @@ handle_info({CloseTag, Socket}, StateName, {stop, {shutdown, transport_closed}, State}; handle_info({ErrorTag, Socket, econnaborted}, StateName, - #state{socket = Socket, start_or_recv_from = StartFrom, role = Role, + #state{socket = Socket, transport_cb = Transport, + start_or_recv_from = StartFrom, role = Role, error_tag = ErrorTag} = State) when StateName =/= connection -> - alert_user(StartFrom, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role), + alert_user(Transport, Socket, StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role), {stop, normal, State}; handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket, @@ -1118,9 +1255,8 @@ init_certificates(#ssl_options{cacerts = CaCerts, end, {ok, _, _, _, _, _} = ssl_manager:connection_init(Certs, Role) catch - Error:Reason -> - handle_file_error(?LINE, Error, Reason, CACertFile, ecacertfile, - erlang:get_stacktrace()) + _:Reason -> + file_error(CACertFile, {cacertfile, Reason}) end, init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CertFile, Role). @@ -1129,7 +1265,9 @@ init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHan init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CertFile, client) -> try - [OwnCert] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle), + %% Ignoring potential proxy-certificates see: + %% http://dev.globus.org/wiki/Security/ProxyFileFormat + [OwnCert|_] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle), {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, OwnCert} catch _Error:_Reason -> {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, undefined} @@ -1137,12 +1275,11 @@ init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHan init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, CertFile, server) -> try - [OwnCert] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle), + [OwnCert|_] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle), {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, OwnCert} catch - Error:Reason -> - handle_file_error(?LINE, Error, Reason, CertFile, ecertfile, - erlang:get_stacktrace()) + _:Reason -> + file_error(CertFile, {certfile, Reason}) end; init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, _, _) -> {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, Cert}. @@ -1155,13 +1292,13 @@ init_private_key(DbHandle, undefined, KeyFile, Password, _) -> [PemEntry] = [PemEntry || PemEntry = {PKey, _ , _} <- List, PKey =:= 'RSAPrivateKey' orelse PKey =:= 'DSAPrivateKey' orelse + PKey =:= 'ECPrivateKey' orelse PKey =:= 'PrivateKeyInfo' ], private_key(public_key:pem_entry_decode(PemEntry, Password)) catch - Error:Reason -> - handle_file_error(?LINE, Error, Reason, KeyFile, ekeyfile, - erlang:get_stacktrace()) + _:Reason -> + file_error(KeyFile, {keyfile, Reason}) end; %% First two clauses are for backwards compatibility @@ -1169,6 +1306,8 @@ init_private_key(_,{rsa, PrivateKey}, _, _,_) -> init_private_key('RSAPrivateKey', PrivateKey); init_private_key(_,{dsa, PrivateKey},_,_,_) -> init_private_key('DSAPrivateKey', PrivateKey); +init_private_key(_,{ec, PrivateKey},_,_,_) -> + init_private_key('ECPrivateKey', PrivateKey); init_private_key(_,{Asn1Type, PrivateKey},_,_,_) -> private_key(init_private_key(Asn1Type, PrivateKey)). @@ -1184,21 +1323,18 @@ private_key(#'PrivateKeyInfo'{privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{algorithm = ?'id-dsa'}, privateKey = Key}) -> public_key:der_decode('DSAPrivateKey', iolist_to_binary(Key)); + private_key(Key) -> Key. --spec(handle_file_error(_,_,_,_,_,_) -> no_return()). -handle_file_error(Line, Error, {badmatch, Reason}, File, Throw, Stack) -> - file_error(Line, Error, Reason, File, Throw, Stack); -handle_file_error(Line, Error, Reason, File, Throw, Stack) -> - file_error(Line, Error, Reason, File, Throw, Stack). - --spec(file_error(_,_,_,_,_,_) -> no_return()). -file_error(Line, Error, Reason, File, Throw, Stack) -> - Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n", - [Line, Error, Reason, File, Stack]), - error_logger:error_report(Report), - throw(Throw). +-spec(file_error(_,_) -> no_return()). +file_error(File, Throw) -> + case Throw of + {Opt,{badmatch, {error, {badmatch, Error}}}} -> + throw({options, {Opt, binary_to_list(File), Error}}); + _ -> + throw(Throw) + end. init_diffie_hellman(_,Params, _,_) when is_binary(Params)-> public_key:der_decode('DHParameter', Params); @@ -1216,9 +1352,8 @@ init_diffie_hellman(DbHandle,_, DHParamFile, server) -> ?DEFAULT_DIFFIE_HELLMAN_PARAMS end catch - Error:Reason -> - handle_file_error(?LINE, Error, Reason, - DHParamFile, edhfile, erlang:get_stacktrace()) + _:Reason -> + file_error(DHParamFile, {dhfile, Reason}) end. sync_send_all_state_event(FsmPid, Event) -> @@ -1240,7 +1375,15 @@ handle_peer_cert(PeerCert, PublicKeyInfo, State1 = State0#state{session = Session#session{peer_certificate = PeerCert}, public_key_info = PublicKeyInfo}, - {Record, State} = next_record(State1), + State2 = case PublicKeyInfo of + {?'id-ecPublicKey', #'ECPoint'{point = _ECPoint} = PublicKey, PublicKeyParams} -> + ECDHKey = public_key:generate_key(PublicKeyParams), + State3 = State1#state{diffie_hellman_keys = ECDHKey}, + ec_dh_master_secret(ECDHKey, PublicKey, State3); + + _ -> State1 + end, + {Record, State} = next_record(State2), next_state(certify, certify, Record, State). certify_client(#state{client_certificate_requested = true, role = client, @@ -1252,7 +1395,7 @@ certify_client(#state{client_certificate_requested = true, role = client, session = #session{own_certificate = OwnCert}, socket = Socket, tls_handshake_history = Handshake0} = State) -> - Certificate = ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client), + Certificate = tls_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client), {BinCert, ConnectionStates, Handshake} = encode_handshake(Certificate, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinCert), @@ -1273,7 +1416,7 @@ verify_client_cert(#state{client_certificate_requested = true, role = client, tls_handshake_history = Handshake0} = State) -> %%TODO: for TLS 1.2 we can choose a different/stronger HashSign combination for this. - case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret, + case tls_handshake:client_certificate_verify(OwnCert, MasterSecret, Version, HashSign, PrivateKey, Handshake0) of #certificate_verify{} = Verified -> {BinVerified, ConnectionStates, Handshake} = @@ -1290,17 +1433,21 @@ verify_client_cert(#state{client_certificate_requested = true, role = client, verify_client_cert(#state{client_certificate_requested = false} = State) -> State. -do_server_hello(Type, #state{negotiated_version = Version, - session = #session{session_id = SessId}, - connection_states = ConnectionStates0, - renegotiation = {Renegotiation, _}} - = State0) when is_atom(Type) -> +do_server_hello(Type, NextProtocolsToSend, + EcPointFormats, EllipticCurves, + #state{negotiated_version = Version, + session = #session{session_id = SessId}, + connection_states = ConnectionStates0, + renegotiation = {Renegotiation, _}} + = State0) when is_atom(Type) -> ServerHello = - ssl_handshake:server_hello(SessId, Version, - ConnectionStates0, Renegotiation), - State = server_hello(ServerHello, State0), - + tls_handshake:server_hello(SessId, Version, + ConnectionStates0, Renegotiation, + NextProtocolsToSend, EcPointFormats, EllipticCurves), + State = server_hello(ServerHello, + State0#state{expecting_next_protocol_negotiation = + NextProtocolsToSend =/= undefined}), case Type of new -> new_server_hello(ServerHello, State); @@ -1331,7 +1478,7 @@ resumed_server_hello(#state{session = Session, connection_states = ConnectionStates0, negotiated_version = Version} = State0) -> - case ssl_handshake:master_secret(Version, Session, + case tls_handshake:master_secret(Version, Session, ConnectionStates0, server) of {_, ConnectionStates1} -> State1 = State0#state{connection_states = ConnectionStates1, @@ -1360,7 +1507,7 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0, session_cache = Cache, session_cache_cb = CacheCb} = State0) -> Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}), - case ssl_handshake:master_secret(Version, Session, + case tls_handshake:master_secret(Version, Session, ConnectionStates0, client) of {_, ConnectionStates} -> {Record, State} = @@ -1420,7 +1567,7 @@ server_hello_done(#state{transport_cb = Transport, connection_states = ConnectionStates0, tls_handshake_history = Handshake0} = State) -> - HelloDone = ssl_handshake:server_hello_done(), + HelloDone = tls_handshake:server_hello_done(), {BinHelloDone, ConnectionStates, Handshake} = encode_handshake(HelloDone, Version, ConnectionStates0, Handshake0), @@ -1428,7 +1575,8 @@ server_hello_done(#state{transport_cb = Transport, State#state{connection_states = ConnectionStates, tls_handshake_history = Handshake}. -certify_server(#state{key_algorithm = dh_anon} = State) -> +certify_server(#state{key_algorithm = Algo} = State) + when Algo == dh_anon; Algo == ecdh_anon; Algo == psk; Algo == dhe_psk; Algo == srp_anon -> State; certify_server(#state{transport_cb = Transport, @@ -1439,7 +1587,7 @@ certify_server(#state{transport_cb = Transport, cert_db = CertDbHandle, cert_db_ref = CertDbRef, session = #session{own_certificate = OwnCert}} = State) -> - case ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of + case tls_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of CertMsg = #certificate{} -> {BinCertMsg, ConnectionStates, Handshake} = encode_handshake(CertMsg, Version, ConnectionStates0, Handshake0), @@ -1455,7 +1603,7 @@ key_exchange(#state{role = server, key_algorithm = rsa} = State) -> State; key_exchange(#state{role = server, key_algorithm = Algo, hashsign_algorithm = HashSignAlgo, - diffie_hellman_params = #'DHParameter'{prime = P, base = G} = Params, + diffie_hellman_params = #'DHParameter'{} = Params, private_key = PrivateKey, connection_states = ConnectionStates0, negotiated_version = Version, @@ -1466,13 +1614,166 @@ key_exchange(#state{role = server, key_algorithm = Algo, when Algo == dhe_dss; Algo == dhe_rsa; Algo == dh_anon -> - Keys = crypto:dh_generate_key([crypto:mpint(P), crypto:mpint(G)]), + DHKeys = public_key:generate_key(Params), ConnectionState = - ssl_record:pending_connection_state(ConnectionStates0, read), + tls_record:pending_connection_state(ConnectionStates0, read), SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Msg = ssl_handshake:key_exchange(server, Version, {dh, Keys, Params, + Msg = tls_handshake:key_exchange(server, Version, {dh, DHKeys, Params, + HashSignAlgo, ClientRandom, + ServerRandom, + PrivateKey}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + diffie_hellman_keys = DHKeys, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = server, private_key = Key, key_algorithm = Algo} = State) + when Algo == ecdh_ecdsa; Algo == ecdh_rsa -> + State#state{diffie_hellman_keys = Key}; +key_exchange(#state{role = server, key_algorithm = Algo, + hashsign_algorithm = HashSignAlgo, + private_key = PrivateKey, + connection_states = ConnectionStates0, + negotiated_version = Version, + tls_handshake_history = Handshake0, + socket = Socket, + transport_cb = Transport + } = State) + when Algo == ecdhe_ecdsa; Algo == ecdhe_rsa; + Algo == ecdh_anon -> + + ECDHKeys = public_key:generate_key(select_curve(State)), + ConnectionState = + tls_record:pending_connection_state(ConnectionStates0, read), + SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{client_random = ClientRandom, + server_random = ServerRandom} = SecParams, + Msg = tls_handshake:key_exchange(server, Version, {ecdh, ECDHKeys, + HashSignAlgo, ClientRandom, + ServerRandom, + PrivateKey}), + {BinMsg, ConnectionStates, Handshake1} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + diffie_hellman_keys = ECDHKeys, + tls_handshake_history = Handshake1}; + +key_exchange(#state{role = server, key_algorithm = psk, + ssl_options = #ssl_options{psk_identity = undefined}} = State) -> + State; +key_exchange(#state{role = server, key_algorithm = psk, + ssl_options = #ssl_options{psk_identity = PskIdentityHint}, + hashsign_algorithm = HashSignAlgo, + private_key = PrivateKey, + connection_states = ConnectionStates0, + negotiated_version = Version, + tls_handshake_history = Handshake0, + socket = Socket, + transport_cb = Transport + } = State) -> + ConnectionState = + tls_record:pending_connection_state(ConnectionStates0, read), + SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{client_random = ClientRandom, + server_random = ServerRandom} = SecParams, + Msg = tls_handshake:key_exchange(server, Version, {psk, PskIdentityHint, + HashSignAlgo, ClientRandom, + ServerRandom, + PrivateKey}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = server, key_algorithm = dhe_psk, + ssl_options = #ssl_options{psk_identity = PskIdentityHint}, + hashsign_algorithm = HashSignAlgo, + diffie_hellman_params = #'DHParameter'{} = Params, + private_key = PrivateKey, + connection_states = ConnectionStates0, + negotiated_version = Version, + tls_handshake_history = Handshake0, + socket = Socket, + transport_cb = Transport + } = State) -> + DHKeys = public_key:generate_key(Params), + ConnectionState = + tls_record:pending_connection_state(ConnectionStates0, read), + SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{client_random = ClientRandom, + server_random = ServerRandom} = SecParams, + Msg = tls_handshake:key_exchange(server, Version, {dhe_psk, PskIdentityHint, DHKeys, Params, + HashSignAlgo, ClientRandom, + ServerRandom, + PrivateKey}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + diffie_hellman_keys = DHKeys, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = server, key_algorithm = rsa_psk, + ssl_options = #ssl_options{psk_identity = undefined}} = State) -> + State; +key_exchange(#state{role = server, key_algorithm = rsa_psk, + ssl_options = #ssl_options{psk_identity = PskIdentityHint}, + hashsign_algorithm = HashSignAlgo, + private_key = PrivateKey, + connection_states = ConnectionStates0, + negotiated_version = Version, + tls_handshake_history = Handshake0, + socket = Socket, + transport_cb = Transport + } = State) -> + ConnectionState = + tls_record:pending_connection_state(ConnectionStates0, read), + SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{client_random = ClientRandom, + server_random = ServerRandom} = SecParams, + Msg = tls_handshake:key_exchange(server, Version, {psk, PskIdentityHint, + HashSignAlgo, ClientRandom, + ServerRandom, + PrivateKey}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = server, key_algorithm = Algo, + ssl_options = #ssl_options{user_lookup_fun = LookupFun}, + hashsign_algorithm = HashSignAlgo, + session = #session{srp_username = Username}, + private_key = PrivateKey, + connection_states = ConnectionStates0, + negotiated_version = Version, + tls_handshake_history = Handshake0, + socket = Socket, + transport_cb = Transport + } = State) + when Algo == srp_dss; + Algo == srp_rsa; + Algo == srp_anon -> + SrpParams = handle_srp_identity(Username, LookupFun), + Keys = case generate_srp_server_keys(SrpParams, 0) of + Alert = #alert{} -> + throw(Alert); + Keys0 = {_,_} -> + Keys0 + end, + ConnectionState = + tls_record:pending_connection_state(ConnectionStates0, read), + SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{client_random = ClientRandom, + server_random = ServerRandom} = SecParams, + Msg = tls_handshake:key_exchange(server, Version, {srp, Keys, SrpParams, HashSignAlgo, ClientRandom, ServerRandom, PrivateKey}), @@ -1480,7 +1781,8 @@ key_exchange(#state{role = server, key_algorithm = Algo, encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates, - diffie_hellman_keys = Keys, + srp_params = SrpParams, + srp_keys = Keys, tls_handshake_history = Handshake}; key_exchange(#state{role = client, @@ -1507,7 +1809,86 @@ key_exchange(#state{role = client, when Algorithm == dhe_dss; Algorithm == dhe_rsa; Algorithm == dh_anon -> - Msg = ssl_handshake:key_exchange(client, Version, {dh, DhPubKey}), + Msg = tls_handshake:key_exchange(client, Version, {dh, DhPubKey}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = client, + connection_states = ConnectionStates0, + key_algorithm = Algorithm, + negotiated_version = Version, + diffie_hellman_keys = Keys, + socket = Socket, transport_cb = Transport, + tls_handshake_history = Handshake0} = State) + when Algorithm == ecdhe_ecdsa; Algorithm == ecdhe_rsa; + Algorithm == ecdh_ecdsa; Algorithm == ecdh_rsa; + Algorithm == ecdh_anon -> + Msg = tls_handshake:key_exchange(client, Version, {ecdh, Keys}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = client, + ssl_options = SslOpts, + connection_states = ConnectionStates0, + key_algorithm = psk, + negotiated_version = Version, + socket = Socket, transport_cb = Transport, + tls_handshake_history = Handshake0} = State) -> + Msg = tls_handshake:key_exchange(client, Version, {psk, SslOpts#ssl_options.psk_identity}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = client, + ssl_options = SslOpts, + connection_states = ConnectionStates0, + key_algorithm = dhe_psk, + negotiated_version = Version, + diffie_hellman_keys = {DhPubKey, _}, + socket = Socket, transport_cb = Transport, + tls_handshake_history = Handshake0} = State) -> + Msg = tls_handshake:key_exchange(client, Version, {dhe_psk, SslOpts#ssl_options.psk_identity, DhPubKey}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = client, + ssl_options = SslOpts, + connection_states = ConnectionStates0, + key_algorithm = rsa_psk, + public_key_info = PublicKeyInfo, + negotiated_version = Version, + premaster_secret = PremasterSecret, + socket = Socket, transport_cb = Transport, + tls_handshake_history = Handshake0} = State) -> + Msg = rsa_psk_key_exchange(Version, SslOpts#ssl_options.psk_identity, PremasterSecret, PublicKeyInfo), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = client, + connection_states = ConnectionStates0, + key_algorithm = Algorithm, + negotiated_version = Version, + srp_keys = {ClientPubKey, _}, + socket = Socket, transport_cb = Transport, + tls_handshake_history = Handshake0} = State) + when Algorithm == srp_dss; + Algorithm == srp_rsa; + Algorithm == srp_anon -> + Msg = tls_handshake:key_exchange(client, Version, {srp, ClientPubKey}), {BinMsg, ConnectionStates, Handshake} = encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), @@ -1524,12 +1905,28 @@ rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) Algorithm == ?sha384WithRSAEncryption; Algorithm == ?sha512WithRSAEncryption -> - ssl_handshake:key_exchange(client, Version, + tls_handshake:key_exchange(client, Version, {premaster_secret, PremasterSecret, PublicKeyInfo}); rsa_key_exchange(_, _, _) -> throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)). +rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) + when Algorithm == ?rsaEncryption; + Algorithm == ?md2WithRSAEncryption; + Algorithm == ?md5WithRSAEncryption; + Algorithm == ?sha1WithRSAEncryption; + Algorithm == ?sha224WithRSAEncryption; + Algorithm == ?sha256WithRSAEncryption; + Algorithm == ?sha384WithRSAEncryption; + Algorithm == ?sha512WithRSAEncryption + -> + tls_handshake:key_exchange(client, Version, + {psk_premaster_secret, PskIdentity, PremasterSecret, + PublicKeyInfo}); +rsa_psk_key_exchange(_, _, _, _) -> + throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)). + request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer}, connection_states = ConnectionStates0, cert_db = CertDbHandle, @@ -1538,7 +1935,7 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer}, negotiated_version = Version, socket = Socket, transport_cb = Transport} = State) -> - Msg = ssl_handshake:certificate_request(ConnectionStates0, CertDbHandle, CertDbRef), + Msg = tls_handshake:certificate_request(ConnectionStates0, CertDbHandle, CertDbRef), {BinMsg, ConnectionStates, Handshake} = encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), @@ -1550,12 +1947,33 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} = State. finalize_handshake(State, StateName) -> - ConnectionStates0 = cipher_protocol(State), + ConnectionStates0 = cipher_protocol(State), + ConnectionStates = - ssl_record:activate_pending_connection_state(ConnectionStates0, + tls_record:activate_pending_connection_state(ConnectionStates0, write), - finished(State#state{connection_states = ConnectionStates}, StateName). - + + State1 = State#state{connection_states = ConnectionStates}, + State2 = next_protocol(State1), + finished(State2, StateName). + +next_protocol(#state{role = server} = State) -> + State; +next_protocol(#state{next_protocol = undefined} = State) -> + State; +next_protocol(#state{expecting_next_protocol_negotiation = false} = State) -> + State; +next_protocol(#state{transport_cb = Transport, socket = Socket, + negotiated_version = Version, + next_protocol = NextProtocol, + connection_states = ConnectionStates0, + tls_handshake_history = Handshake0} = State) -> + NextProtocolMessage = tls_handshake:next_protocol(NextProtocol), + {BinMsg, ConnectionStates, Handshake} = encode_handshake(NextProtocolMessage, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}. + cipher_protocol(#state{connection_states = ConnectionStates0, socket = Socket, negotiated_version = Version, @@ -1572,7 +1990,7 @@ finished(#state{role = Role, socket = Socket, negotiated_version = Version, connection_states = ConnectionStates0, tls_handshake_history = Handshake0}, StateName) -> MasterSecret = Session#session.master_secret, - Finished = ssl_handshake:finished(Version, Role, + Finished = tls_handshake:finished(Version, Role, get_current_connection_state_prf(ConnectionStates0, write), MasterSecret, Handshake0), ConnectionStates1 = save_verify_data(Role, Finished, ConnectionStates0, StateName), @@ -1582,87 +2000,79 @@ finished(#state{role = Role, socket = Socket, negotiated_version = Version, {ConnectionStates, Handshake}. save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, certify) -> - ssl_record:set_client_verify_data(current_write, Data, ConnectionStates); + tls_record:set_client_verify_data(current_write, Data, ConnectionStates); save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, cipher) -> - ssl_record:set_server_verify_data(current_both, Data, ConnectionStates); + tls_record:set_server_verify_data(current_both, Data, ConnectionStates); save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, abbreviated) -> - ssl_record:set_client_verify_data(current_both, Data, ConnectionStates); + tls_record:set_client_verify_data(current_both, Data, ConnectionStates); save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, abbreviated) -> - ssl_record:set_server_verify_data(current_write, Data, ConnectionStates). - -handle_server_key(#server_key_exchange{params = - #server_dh_params{dh_p = P, - dh_g = G, - dh_y = ServerPublicDhKey}, - signed_params = <<>>}, - #state{key_algorithm = dh_anon} = State) -> - dh_master_secret(P, G, ServerPublicDhKey, undefined, State); + tls_record:set_server_verify_data(current_write, Data, ConnectionStates). -handle_server_key( - #server_key_exchange{params = - #server_dh_params{dh_p = P, - dh_g = G, - dh_y = ServerPublicDhKey}, - signed_params = Signed, - hashsign = HashSign}, - #state{negotiated_version = Version, - public_key_info = PubKeyInfo, - connection_states = ConnectionStates} = State) -> - - PLen = size(P), - GLen = size(G), - YLen = size(ServerPublicDhKey), - HashAlgo = connection_hash_algo(HashSign, State), +handle_server_key(#server_key_exchange{exchange_keys = Keys}, + #state{key_algorithm = KeyAlg, + negotiated_version = Version} = State) -> + Params = tls_handshake:decode_server_key(Keys, KeyAlg, Version), + HashSign = connection_hashsign(Params#server_key_params.hashsign, State), + case HashSign of + {_, SignAlgo} when SignAlgo == anon; SignAlgo == ecdh_anon -> + server_master_secret(Params#server_key_params.params, State); + _ -> + verify_server_key(Params, HashSign, State) + end. - ConnectionState = - ssl_record:pending_connection_state(ConnectionStates, read), +verify_server_key(#server_key_params{params = Params, + params_bin = EncParams, + signature = Signature}, + HashSign = {HashAlgo, _}, + #state{negotiated_version = Version, + public_key_info = PubKeyInfo, + connection_states = ConnectionStates} = State) -> + ConnectionState = + tls_record:pending_connection_state(ConnectionStates, read), SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Hash = ssl_handshake:server_key_exchange_hash(HashAlgo, - <<ClientRandom/binary, - ServerRandom/binary, - ?UINT16(PLen), P/binary, - ?UINT16(GLen), G/binary, - ?UINT16(YLen), - ServerPublicDhKey/binary>>), - - case verify_dh_params(Version, Signed, Hash, HashAlgo, PubKeyInfo) of + Hash = tls_handshake:server_key_exchange_hash(HashAlgo, + <<ClientRandom/binary, + ServerRandom/binary, + EncParams/binary>>), + case tls_handshake:verify_signature(Version, Hash, HashSign, Signature, PubKeyInfo) of true -> - dh_master_secret(P, G, ServerPublicDhKey, undefined, State); + server_master_secret(Params, State); false -> ?ALERT_REC(?FATAL, ?DECRYPT_ERROR) end. -verify_dh_params({3, Minor}, Signed, Hashes, HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams}) - when Minor >= 3 -> - public_key:verify({digest, Hashes}, HashAlgo, Signed, PubKey); -verify_dh_params(_Version, Signed, Hashes, _HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams}) -> - case public_key:decrypt_public(Signed, PubKey, - [{rsa_pad, rsa_pkcs1_padding}]) of - Hashes -> - true; - _ -> - false - end; -verify_dh_params(_Version, Signed, Hash, HashAlgo, {?'id-dsa', PublicKey, PublicKeyParams}) -> - public_key:verify({digest, Hash}, HashAlgo, Signed, {PublicKey, PublicKeyParams}). - -dh_master_secret(Prime, Base, PublicDhKey, undefined, State) -> - PMpint = mpint_binary(Prime), - GMpint = mpint_binary(Base), - Keys = {_, PrivateDhKey} = - crypto:dh_generate_key([PMpint,GMpint]), - dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys}); +server_master_secret(#server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDhKey}, + State) -> + dh_master_secret(P, G, ServerPublicDhKey, undefined, State); -dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, - #state{session = Session, - negotiated_version = Version, role = Role, - connection_states = ConnectionStates0} = State) -> - PremasterSecret = - crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey, - [PMpint, GMpint]), - case ssl_handshake:master_secret(Version, PremasterSecret, +server_master_secret(#server_ecdh_params{curve = ECCurve, public = ECServerPubKey}, + State) -> + ECDHKeys = public_key:generate_key(ECCurve), + ec_dh_master_secret(ECDHKeys, #'ECPoint'{point = ECServerPubKey}, State#state{diffie_hellman_keys = ECDHKeys}); + +server_master_secret(#server_psk_params{ + hint = IdentityHint}, + State) -> + %% store for later use + State#state{psk_identity = IdentityHint}; + +server_master_secret(#server_dhe_psk_params{ + hint = IdentityHint, + dh_params = #server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDhKey}}, + State) -> + dhe_psk_master_secret(IdentityHint, P, G, ServerPublicDhKey, undefined, State); + +server_master_secret(#server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B}, + State) -> + client_srp_master_secret(G, N, S, B, undefined, State). + +master_from_premaster_secret(PremasterSecret, + #state{session = Session, + negotiated_version = Version, role = Role, + connection_states = ConnectionStates0} = State) -> + case tls_handshake:master_secret(Version, PremasterSecret, ConnectionStates0, Role) of {MasterSecret, ConnectionStates} -> State#state{ @@ -1673,13 +2083,154 @@ dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, Alert end. +dh_master_secret(#'DHParameter'{} = Params, OtherPublicDhKey, MyPrivateKey, State) -> + PremasterSecret = + public_key:compute_key(OtherPublicDhKey, MyPrivateKey, Params), + master_from_premaster_secret(PremasterSecret, State). + +dh_master_secret(Prime, Base, PublicDhKey, undefined, State) -> + Keys = {_, PrivateDhKey} = crypto:generate_key(dh, [Prime, Base]), + dh_master_secret(Prime, Base, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys}); + +dh_master_secret(Prime, Base, PublicDhKey, PrivateDhKey, State) -> + PremasterSecret = + crypto:compute_key(dh, PublicDhKey, PrivateDhKey, [Prime, Base]), + master_from_premaster_secret(PremasterSecret, State). + +ec_dh_master_secret(ECDHKeys, ECPoint, State) -> + PremasterSecret = + public_key:compute_key(ECPoint, ECDHKeys), + master_from_premaster_secret(PremasterSecret, State). + +handle_psk_identity(_PSKIdentity, LookupFun) + when LookupFun == undefined -> + error; +handle_psk_identity(PSKIdentity, {Fun, UserState}) -> + Fun(psk, PSKIdentity, UserState). + +server_psk_master_secret(ClientPSKIdentity, + #state{ssl_options = SslOpts} = State) -> + case handle_psk_identity(ClientPSKIdentity, SslOpts#ssl_options.user_lookup_fun) of + {ok, PSK} when is_binary(PSK) -> + Len = byte_size(PSK), + PremasterSecret = <<?UINT16(Len), 0:(Len*8), ?UINT16(Len), PSK/binary>>, + master_from_premaster_secret(PremasterSecret, State); + #alert{} = Alert -> + Alert; + _ -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + end. + +dhe_psk_master_secret(PSKIdentity, Prime, Base, PublicDhKey, undefined, State) -> + Keys = {_, PrivateDhKey} = + crypto:generate_key(dh, [Prime, Base]), + dhe_psk_master_secret(PSKIdentity, Prime, Base, PublicDhKey, PrivateDhKey, + State#state{diffie_hellman_keys = Keys}); + +dhe_psk_master_secret(PSKIdentity, Prime, Base, PublicDhKey, PrivateDhKey, + #state{ssl_options = SslOpts} = State) -> + case handle_psk_identity(PSKIdentity, SslOpts#ssl_options.user_lookup_fun) of + {ok, PSK} when is_binary(PSK) -> + DHSecret = + crypto:compute_key(dh, PublicDhKey, PrivateDhKey, + [Prime, Base]), + DHLen = erlang:byte_size(DHSecret), + Len = erlang:byte_size(PSK), + PremasterSecret = <<?UINT16(DHLen), DHSecret/binary, ?UINT16(Len), PSK/binary>>, + master_from_premaster_secret(PremasterSecret, State); + #alert{} = Alert -> + Alert; + _ -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + end. + +server_rsa_psk_master_secret(PskIdentity, PremasterSecret, + #state{ssl_options = SslOpts} = State) -> + case handle_psk_identity(PskIdentity, SslOpts#ssl_options.user_lookup_fun) of + {ok, PSK} when is_binary(PSK) -> + Len = byte_size(PSK), + RealPMS = <<?UINT16(48), PremasterSecret/binary, ?UINT16(Len), PSK/binary>>, + master_from_premaster_secret(RealPMS, State); + #alert{} = Alert -> + Alert; + _ -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + end. + +generate_srp_server_keys(_SrpParams, 10) -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); +generate_srp_server_keys(SrpParams = + #srp_user{generator = Generator, prime = Prime, + verifier = Verifier}, N) -> + case crypto:generate_key(srp, {host, [Verifier, Generator, Prime, '6a']}) of + error -> + generate_srp_server_keys(SrpParams, N+1); + Keys -> + Keys + end. + +generate_srp_client_keys(_Generator, _Prime, 10) -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); +generate_srp_client_keys(Generator, Prime, N) -> + + case crypto:generate_key(srp, {user, [Generator, Prime, '6a']}) of + error -> + generate_srp_client_keys(Generator, Prime, N+1); + Keys -> + Keys + end. + +handle_srp_identity(Username, {Fun, UserState}) -> + case Fun(srp, Username, UserState) of + {ok, {SRPParams, Salt, DerivedKey}} + when is_atom(SRPParams), is_binary(Salt), is_binary(DerivedKey) -> + {Generator, Prime} = ssl_srp_primes:get_srp_params(SRPParams), + Verifier = crypto:mod_pow(Generator, DerivedKey, Prime), + #srp_user{generator = Generator, prime = Prime, + salt = Salt, verifier = Verifier}; + #alert{} = Alert -> + throw(Alert); + _ -> + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)) + end. + +server_srp_master_secret(Verifier, Prime, ClientPub, State = #state{srp_keys = ServerKeys}) -> + case crypto:compute_key(srp, ClientPub, ServerKeys, {host, [Verifier, Prime, '6a']}) of + error -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + PremasterSecret -> + master_from_premaster_secret(PremasterSecret, State) + end. + +client_srp_master_secret(_Generator, _Prime, _Salt, _ServerPub, #alert{} = Alert, _State) -> + Alert; +client_srp_master_secret(Generator, Prime, Salt, ServerPub, undefined, State) -> + Keys = generate_srp_client_keys(Generator, Prime, 0), + client_srp_master_secret(Generator, Prime, Salt, ServerPub, Keys, State#state{srp_keys = Keys}); + +client_srp_master_secret(Generator, Prime, Salt, ServerPub, ClientKeys, + #state{ssl_options = SslOpts} = State) -> + case ssl_srp_primes:check_srp_params(Generator, Prime) of + ok -> + {Username, Password} = SslOpts#ssl_options.srp_identity, + DerivedKey = crypto:hash(sha, [Salt, crypto:hash(sha, [Username, <<$:>>, Password])]), + case crypto:compute_key(srp, ServerPub, ClientKeys, {user, [DerivedKey, Prime, Generator, '6a']}) of + error -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + PremasterSecret -> + master_from_premaster_secret(PremasterSecret, State) + end; + _ -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + end. + cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} = State) -> - ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0), + ConnectionStates = tls_record:set_server_verify_data(current_both, Data, ConnectionStates0), next_state_connection(cipher, ack_connection(State#state{session = Session, connection_states = ConnectionStates})); cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State) -> - ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0), + ConnectionStates1 = tls_record:set_client_verify_data(current_read, Data, ConnectionStates0), {ConnectionStates, Handshake} = finalize_handshake(State#state{connection_states = ConnectionStates1, session = Session}, cipher), @@ -1689,16 +2240,16 @@ cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0 tls_handshake_history = Handshake})). encode_alert(#alert{} = Alert, Version, ConnectionStates) -> - ssl_record:encode_alert_record(Alert, Version, ConnectionStates). + tls_record:encode_alert_record(Alert, Version, ConnectionStates). encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> - ssl_record:encode_change_cipher_spec(Version, ConnectionStates). + tls_record:encode_change_cipher_spec(Version, ConnectionStates). encode_handshake(HandshakeRec, Version, ConnectionStates0, Handshake0) -> - Frag = ssl_handshake:encode_handshake(HandshakeRec, Version), - Handshake1 = ssl_handshake:update_handshake_history(Handshake0, Frag), + Frag = tls_handshake:encode_handshake(HandshakeRec, Version), + Handshake1 = tls_handshake:update_handshake_history(Handshake0, Frag), {E, ConnectionStates1} = - ssl_record:encode_handshake(Frag, Version, ConnectionStates0), + tls_record:encode_handshake(Frag, Version, ConnectionStates0), {E, ConnectionStates1, Handshake1}. encode_packet(Data, #socket_options{packet=Packet}) -> @@ -1740,6 +2291,8 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) -> end. read_application_data(Data, #state{user_application = {_Mon, Pid}, + socket = Socket, + transport_cb = Transport, socket_options = SOpts, bytes_to_read = BytesToRead, start_or_recv_from = RecvFrom, @@ -1752,7 +2305,7 @@ read_application_data(Data, #state{user_application = {_Mon, Pid}, end, case get_data(SOpts, BytesToRead, Buffer1) of {ok, ClientData, Buffer} -> % Send data - SocketOpt = deliver_app_data(SOpts, ClientData, Pid, RecvFrom), + SocketOpt = deliver_app_data(Transport, Socket, SOpts, ClientData, Pid, RecvFrom), cancel_timer(Timer), State = State0#state{user_data_buffer = Buffer, start_or_recv_from = undefined, @@ -1773,7 +2326,7 @@ read_application_data(Data, #state{user_application = {_Mon, Pid}, {passive, Buffer} -> next_record_if_active(State0#state{user_data_buffer = Buffer}); {error,_Reason} -> %% Invalid packet in packet mode - deliver_packet_error(SOpts, Buffer1, Pid, RecvFrom), + deliver_packet_error(Transport, Socket, SOpts, Buffer1, Pid, RecvFrom), {stop, normal, State0} end. @@ -1791,7 +2344,7 @@ write_application_data(Data0, From, #state{socket = Socket, renegotiate(State#state{send_queue = queue:in_r({From, Data}, SendQueue), renegotiation = {true, internal}}); false -> - {Msgs, ConnectionStates} = ssl_record:encode_data(Data, Version, ConnectionStates0), + {Msgs, ConnectionStates} = tls_record:encode_data(Data, Version, ConnectionStates0), Result = Transport:send(Socket, Msgs), {reply, Result, connection, State#state{connection_states = ConnectionStates}, get_timeout(State)} @@ -1855,9 +2408,9 @@ decode_packet(Type, Buffer, PacketOpts) -> %% Note that if the user has explicitly configured the socket to expect %% HTTP headers using the {packet, httph} option, we don't do any automatic %% switching of states. -deliver_app_data(SOpts = #socket_options{active=Active, packet=Type}, - Data, Pid, From) -> - send_or_reply(Active, Pid, From, format_reply(SOpts, Data)), +deliver_app_data(Transport, Socket, SOpts = #socket_options{active=Active, packet=Type}, + Data, Pid, From) -> + send_or_reply(Active, Pid, From, format_reply(Transport, Socket, SOpts, Data)), SO = case Data of {P, _, _, _} when ((P =:= http_request) or (P =:= http_response)), ((Type =:= http) or (Type =:= http_bin)) -> @@ -1876,31 +2429,31 @@ deliver_app_data(SOpts = #socket_options{active=Active, packet=Type}, SO end. -format_reply(#socket_options{active = false, mode = Mode, packet = Packet, +format_reply(_, _,#socket_options{active = false, mode = Mode, packet = Packet, header = Header}, Data) -> - {ok, format_reply(Mode, Packet, Header, Data)}; -format_reply(#socket_options{active = _, mode = Mode, packet = Packet, - header = Header}, Data) -> - {ssl, sslsocket(), format_reply(Mode, Packet, Header, Data)}. + {ok, do_format_reply(Mode, Packet, Header, Data)}; +format_reply(Transport, Socket, #socket_options{active = _, mode = Mode, packet = Packet, + header = Header}, Data) -> + {ssl, ssl_socket:socket(self(), Transport, Socket), do_format_reply(Mode, Packet, Header, Data)}. -deliver_packet_error(SO= #socket_options{active = Active}, Data, Pid, From) -> - send_or_reply(Active, Pid, From, format_packet_error(SO, Data)). +deliver_packet_error(Transport, Socket, SO= #socket_options{active = Active}, Data, Pid, From) -> + send_or_reply(Active, Pid, From, format_packet_error(Transport, Socket, SO, Data)). -format_packet_error(#socket_options{active = false, mode = Mode}, Data) -> - {error, {invalid_packet, format_reply(Mode, raw, 0, Data)}}; -format_packet_error(#socket_options{active = _, mode = Mode}, Data) -> - {ssl_error, sslsocket(), {invalid_packet, format_reply(Mode, raw, 0, Data)}}. +format_packet_error(_, _,#socket_options{active = false, mode = Mode}, Data) -> + {error, {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}; +format_packet_error(Transport, Socket, #socket_options{active = _, mode = Mode}, Data) -> + {ssl_error, ssl_socket:socket(self(), Transport, Socket), {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}. -format_reply(binary, _, N, Data) when N > 0 -> % Header mode +do_format_reply(binary, _, N, Data) when N > 0 -> % Header mode header(N, Data); -format_reply(binary, _, _, Data) -> +do_format_reply(binary, _, _, Data) -> Data; -format_reply(list, Packet, _, Data) +do_format_reply(list, Packet, _, Data) when Packet == http; Packet == {http, headers}; Packet == http_bin; Packet == {http_bin, headers}; Packet == httph; Packet == httph_bin -> Data; -format_reply(list, _,_, Data) -> +do_format_reply(list, _,_, Data) -> binary_to_list(Data). header(0, <<>>) -> @@ -1959,7 +2512,7 @@ next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) -> %% This message should not be included in handshake %% message hashes. Starts new handshake (renegotiation) - Hs0 = ssl_handshake:init_handshake_history(), + Hs0 = tls_handshake:init_handshake_history(), ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs0, renegotiation = {true, peer}}); ({#hello_request{} = Packet, _}, {next_state, SName, State}) -> @@ -1968,17 +2521,17 @@ next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, ?MODULE:SName(Packet, State); ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) -> Version = Packet#client_hello.client_version, - Hs0 = ssl_handshake:init_handshake_history(), - Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw), + Hs0 = tls_handshake:init_handshake_history(), + Hs1 = tls_handshake:update_handshake_history(Hs0, Raw), ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1, renegotiation = {true, peer}}); ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_history=Hs0}}) -> - Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw), + Hs1 = tls_handshake:update_handshake_history(Hs0, Raw), ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1}); (_, StopState) -> StopState end, try - {Packets, Buf} = ssl_handshake:get_tls_handshake(Version,Data,Buf0), + {Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0), State = State0#state{tls_packets = Packets, tls_handshake_buffer = Buf}, handle_tls_handshake(Handle, Next, State) catch throw:#alert{} = Alert -> @@ -1996,7 +2549,7 @@ next_state(Current, Next, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>} _ChangeCipher, #state{connection_states = ConnectionStates0} = State0) -> ConnectionStates1 = - ssl_record:activate_pending_connection_state(ConnectionStates0, read), + tls_record:activate_pending_connection_state(ConnectionStates0, read), {Record, State} = next_record(State0#state{connection_states = ConnectionStates1}), next_state(Current, Next, Record, State); next_state(Current, Next, #ssl_tls{type = _Unknown}, State0) -> @@ -2006,7 +2559,7 @@ next_state(Current, Next, #ssl_tls{type = _Unknown}, State0) -> next_tls_record(Data, #state{tls_record_buffer = Buf0, tls_cipher_texts = CT0} = State0) -> - case ssl_record:get_tls_records(Data, Buf0) of + case tls_record:get_tls_records(Data, Buf0) of {Records, Buf1} -> CT1 = CT0 ++ Records, next_record(State0#state{tls_record_buffer = Buf1, @@ -2015,12 +2568,13 @@ next_tls_record(Data, #state{tls_record_buffer = Buf0, Alert end. -next_record(#state{tls_packets = [], tls_cipher_texts = [], socket = Socket} = State) -> - inet:setopts(Socket, [{active,once}]), +next_record(#state{tls_packets = [], tls_cipher_texts = [], socket = Socket, + transport_cb = Transport} = State) -> + ssl_socket:setopts(Transport, Socket, [{active,once}]), {no_record, State}; next_record(#state{tls_packets = [], tls_cipher_texts = [CT | Rest], connection_states = ConnStates0} = State) -> - case ssl_record:decode_cipher_text(CT, ConnStates0) of + case tls_record:decode_cipher_text(CT, ConnStates0) of {Plain, ConnStates} -> {Plain, State#state{tls_cipher_texts = Rest, connection_states = ConnStates}}; #alert{} = Alert -> @@ -2047,7 +2601,7 @@ next_state_connection(StateName, #state{send_queue = Queue0, case queue:out(Queue0) of {{value, {From, Data}}, Queue} -> {Msgs, ConnectionStates} = - ssl_record:encode_data(Data, Version, ConnectionStates0), + tls_record:encode_data(Data, Version, ConnectionStates0), Result = Transport:send(Socket, Msgs), gen_fsm:reply(From, Result), next_state_connection(StateName, @@ -2066,13 +2620,13 @@ next_state_is_connection(_, State = #socket_options{active = false}}) when RecvFrom =/= undefined -> passive_receive(State#state{premaster_secret = undefined, public_key_info = undefined, - tls_handshake_history = ssl_handshake:init_handshake_history()}, connection); + tls_handshake_history = tls_handshake:init_handshake_history()}, connection); next_state_is_connection(StateName, State0) -> {Record, State} = next_record_if_active(State0), next_state(StateName, connection, Record, State#state{premaster_secret = undefined, - public_key_info = undefined, - tls_handshake_history = ssl_handshake:init_handshake_history()}). + public_key_info = undefined, + tls_handshake_history = tls_handshake:init_handshake_history()}). register_session(client, Host, Port, #session{is_resumable = new} = Session0) -> Session = Session0#session{is_resumable = true}, @@ -2092,7 +2646,7 @@ invalidate_session(server, _, Port, Session) -> initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, {CbModule, DataTag, CloseTag, ErrorTag}) -> - ConnectionStates = ssl_record:init_connection_states(Role), + ConnectionStates = tls_record:init_connection_states(Role), SessionCacheCb = case application:get_env(ssl, session_cb) of {ok, Cb} when is_atom(Cb) -> @@ -2128,64 +2682,58 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, send_queue = queue:new() }. -sslsocket(Pid) -> - #sslsocket{pid = Pid, fd = new_ssl}. - -sslsocket() -> - sslsocket(self()). - -get_socket_opts(_,[], _, Acc) -> +get_socket_opts(_,_,[], _, Acc) -> {ok, Acc}; -get_socket_opts(Socket, [mode | Tags], SockOpts, Acc) -> - get_socket_opts(Socket, Tags, SockOpts, +get_socket_opts(Transport, Socket, [mode | Tags], SockOpts, Acc) -> + get_socket_opts(Transport, Socket, Tags, SockOpts, [{mode, SockOpts#socket_options.mode} | Acc]); -get_socket_opts(Socket, [packet | Tags], SockOpts, Acc) -> +get_socket_opts(Transport, Socket, [packet | Tags], SockOpts, Acc) -> case SockOpts#socket_options.packet of {Type, headers} -> - get_socket_opts(Socket, Tags, SockOpts, [{packet, Type} | Acc]); + get_socket_opts(Transport, Socket, Tags, SockOpts, [{packet, Type} | Acc]); Type -> - get_socket_opts(Socket, Tags, SockOpts, [{packet, Type} | Acc]) + get_socket_opts(Transport, Socket, Tags, SockOpts, [{packet, Type} | Acc]) end; -get_socket_opts(Socket, [header | Tags], SockOpts, Acc) -> - get_socket_opts(Socket, Tags, SockOpts, +get_socket_opts(Transport, Socket, [header | Tags], SockOpts, Acc) -> + get_socket_opts(Transport, Socket, Tags, SockOpts, [{header, SockOpts#socket_options.header} | Acc]); -get_socket_opts(Socket, [active | Tags], SockOpts, Acc) -> - get_socket_opts(Socket, Tags, SockOpts, +get_socket_opts(Transport, Socket, [active | Tags], SockOpts, Acc) -> + get_socket_opts(Transport, Socket, Tags, SockOpts, [{active, SockOpts#socket_options.active} | Acc]); -get_socket_opts(Socket, [Tag | Tags], SockOpts, Acc) -> - try inet:getopts(Socket, [Tag]) of +get_socket_opts(Transport, Socket, [Tag | Tags], SockOpts, Acc) -> + try ssl_socket:getopts(Transport, Socket, [Tag]) of {ok, [Opt]} -> - get_socket_opts(Socket, Tags, SockOpts, [Opt | Acc]); + get_socket_opts(Transport, Socket, Tags, SockOpts, [Opt | Acc]); {error, Error} -> - {error, {eoptions, {inet_option, Tag, Error}}} + {error, {options, {socket_options, Tag, Error}}} catch %% So that inet behavior does not crash our process - _:Error -> {error, {eoptions, {inet_option, Tag, Error}}} + _:Error -> {error, {options, {socket_options, Tag, Error}}} end; -get_socket_opts(_,Opts, _,_) -> - {error, {eoptions, {inet_option, Opts, function_clause}}}. +get_socket_opts(_, _,Opts, _,_) -> + {error, {options, {socket_options, Opts, function_clause}}}. -set_socket_opts(_, [], SockOpts, []) -> +set_socket_opts(_,_, [], SockOpts, []) -> {ok, SockOpts}; -set_socket_opts(Socket, [], SockOpts, Other) -> +set_socket_opts(Transport, Socket, [], SockOpts, Other) -> %% Set non emulated options - try inet:setopts(Socket, Other) of + try ssl_socket:setopts(Transport, Socket, Other) of ok -> {ok, SockOpts}; {error, InetError} -> - {{error, {eoptions, {inet_options, Other, InetError}}}, SockOpts} + {{error, {options, {socket_options, Other, InetError}}}, SockOpts} catch _:Error -> %% So that inet behavior does not crash our process - {{error, {eoptions, {inet_options, Other, Error}}}, SockOpts} + {{error, {options, {socket_options, Other, Error}}}, SockOpts} end; -set_socket_opts(Socket, [{mode, Mode}| Opts], SockOpts, Other) when Mode == list; Mode == binary -> - set_socket_opts(Socket, Opts, +set_socket_opts(Transport,Socket, [{mode, Mode}| Opts], SockOpts, Other) when Mode == list; Mode == binary -> + set_socket_opts(Transport, Socket, Opts, SockOpts#socket_options{mode = Mode}, Other); -set_socket_opts(_, [{mode, _} = Opt| _], SockOpts, _) -> - {{error, {eoptions, {inet_opt, Opt}}}, SockOpts}; -set_socket_opts(Socket, [{packet, Packet}| Opts], SockOpts, Other) when Packet == raw; +set_socket_opts(_, _, [{mode, _} = Opt| _], SockOpts, _) -> + {{error, {options, {socket_options, Opt}}}, SockOpts}; +set_socket_opts(Transport,Socket, [{packet, Packet}| Opts], SockOpts, Other) when Packet == raw; Packet == 0; Packet == 1; Packet == 2; @@ -2200,24 +2748,24 @@ set_socket_opts(Socket, [{packet, Packet}| Opts], SockOpts, Other) when Packet = Packet == httph; Packet == http_bin; Packet == httph_bin -> - set_socket_opts(Socket, Opts, + set_socket_opts(Transport, Socket, Opts, SockOpts#socket_options{packet = Packet}, Other); -set_socket_opts(_, [{packet, _} = Opt| _], SockOpts, _) -> - {{error, {eoptions, {inet_opt, Opt}}}, SockOpts}; -set_socket_opts(Socket, [{header, Header}| Opts], SockOpts, Other) when is_integer(Header) -> - set_socket_opts(Socket, Opts, +set_socket_opts(_, _, [{packet, _} = Opt| _], SockOpts, _) -> + {{error, {options, {socket_options, Opt}}}, SockOpts}; +set_socket_opts(Transport, Socket, [{header, Header}| Opts], SockOpts, Other) when is_integer(Header) -> + set_socket_opts(Transport, Socket, Opts, SockOpts#socket_options{header = Header}, Other); -set_socket_opts(_, [{header, _} = Opt| _], SockOpts, _) -> - {{error,{eoptions, {inet_opt, Opt}}}, SockOpts}; -set_socket_opts(Socket, [{active, Active}| Opts], SockOpts, Other) when Active == once; - Active == true; - Active == false -> - set_socket_opts(Socket, Opts, +set_socket_opts(_, _, [{header, _} = Opt| _], SockOpts, _) -> + {{error,{options, {socket_options, Opt}}}, SockOpts}; +set_socket_opts(Transport, Socket, [{active, Active}| Opts], SockOpts, Other) when Active == once; + Active == true; + Active == false -> + set_socket_opts(Transport, Socket, Opts, SockOpts#socket_options{active = Active}, Other); -set_socket_opts(_, [{active, _} = Opt| _], SockOpts, _) -> - {{error, {eoptions, {inet_opt, Opt}} }, SockOpts}; -set_socket_opts(Socket, [Opt | Opts], SockOpts, Other) -> - set_socket_opts(Socket, Opts, SockOpts, [Opt | Other]). +set_socket_opts(_, _, [{active, _} = Opt| _], SockOpts, _) -> + {{error, {options, {socket_options, Opt}} }, SockOpts}; +set_socket_opts(Transport, Socket, [Opt | Opts], SockOpts, Other) -> + set_socket_opts(Transport, Socket, Opts, SockOpts, [Opt | Other]). handle_alerts([], Result) -> Result; @@ -2228,11 +2776,12 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State, _Timeout}) -> handle_alerts(Alerts, handle_alert(Alert, StateName, State)). handle_alert(#alert{level = ?FATAL} = Alert, StateName, - #state{ssl_options = SslOpts, start_or_recv_from = From, host = Host, port = Port, session = Session, - user_application = {_Mon, Pid}, role = Role, socket_options = Opts} = State) -> + #state{socket = Socket, transport_cb = Transport, ssl_options = SslOpts, start_or_recv_from = From, host = Host, + port = Port, session = Session, user_application = {_Mon, Pid}, + role = Role, socket_options = Opts} = State) -> invalidate_session(Role, Host, Port, Session), log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), - alert_user(StateName, Opts, Pid, From, Alert, Role), + alert_user(Transport, Socket, StateName, Opts, Pid, From, Alert, Role), {stop, normal, State}; handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, @@ -2259,28 +2808,28 @@ handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, Sta {Record, State} = next_record(State0), next_state(StateName, StateName, Record, State). -alert_user(connection, Opts, Pid, From, Alert, Role) -> - alert_user(Opts#socket_options.active, Pid, From, Alert, Role); -alert_user(_, _, _, From, Alert, Role) -> - alert_user(From, Alert, Role). +alert_user(Transport, Socket, connection, Opts, Pid, From, Alert, Role) -> + alert_user(Transport,Socket, Opts#socket_options.active, Pid, From, Alert, Role); +alert_user(Transport, Socket,_, _, _, From, Alert, Role) -> + alert_user(Transport, Socket, From, Alert, Role). -alert_user(From, Alert, Role) -> - alert_user(false, no_pid, From, Alert, Role). +alert_user(Transport, Socket, From, Alert, Role) -> + alert_user(Transport, Socket, false, no_pid, From, Alert, Role). -alert_user(false = Active, Pid, From, Alert, Role) -> +alert_user(_,_, false = Active, Pid, From, Alert, Role) -> %% If there is an outstanding ssl_accept | recv %% From will be defined and send_or_reply will %% send the appropriate error message. ReasonCode = ssl_alert:reason_code(Alert, Role), send_or_reply(Active, Pid, From, {error, ReasonCode}); -alert_user(Active, Pid, From, Alert, Role) -> +alert_user(Transport, Socket, Active, Pid, From, Alert, Role) -> case ssl_alert:reason_code(Alert, Role) of closed -> send_or_reply(Active, Pid, From, - {ssl_closed, sslsocket()}); + {ssl_closed, ssl_socket:socket(self(), Transport, Socket)}); ReasonCode -> send_or_reply(Active, Pid, From, - {ssl_error, sslsocket(), ReasonCode}) + {ssl_error, ssl_socket:socket(self(), Transport, Socket), ReasonCode}) end. log_alert(true, Info, Alert) -> @@ -2310,13 +2859,18 @@ handle_own_alert(Alert, Version, StateName, end, {stop, {shutdown, own_alert}, State}. -handle_normal_shutdown(Alert, _, #state{start_or_recv_from = StartFrom, role = Role, renegotiation = {false, first}}) -> - alert_user(StartFrom, Alert, Role); +handle_normal_shutdown(Alert, _, #state{socket = Socket, + transport_cb = Transport, + start_or_recv_from = StartFrom, + role = Role, renegotiation = {false, first}}) -> + alert_user(Transport, Socket, StartFrom, Alert, Role); -handle_normal_shutdown(Alert, StateName, #state{socket_options = Opts, +handle_normal_shutdown(Alert, StateName, #state{socket = Socket, + socket_options = Opts, + transport_cb = Transport, user_application = {_Mon, Pid}, start_or_recv_from = RecvFrom, role = Role}) -> - alert_user(StateName, Opts, Pid, RecvFrom, Alert, Role). + alert_user(Transport, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role). handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) -> Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), @@ -2328,11 +2882,6 @@ make_premaster_secret({MajVer, MinVer}, rsa) -> make_premaster_secret(_, _) -> undefined. -mpint_binary(Binary) -> - Size = erlang:byte_size(Binary), - <<?UINT32(Size), Binary/binary>>. - - ack_connection(#state{renegotiation = {true, Initiater}} = State) when Initiater == internal; Initiater == peer -> @@ -2352,18 +2901,18 @@ ack_connection(State) -> renegotiate(#state{role = client} = State) -> %% Handle same way as if server requested %% the renegotiation - Hs0 = ssl_handshake:init_handshake_history(), + Hs0 = tls_handshake:init_handshake_history(), connection(#hello_request{}, State#state{tls_handshake_history = Hs0}); renegotiate(#state{role = server, socket = Socket, transport_cb = Transport, negotiated_version = Version, connection_states = ConnectionStates0} = State0) -> - HelloRequest = ssl_handshake:hello_request(), - Frag = ssl_handshake:encode_handshake(HelloRequest, Version), - Hs0 = ssl_handshake:init_handshake_history(), + HelloRequest = tls_handshake:hello_request(), + Frag = tls_handshake:encode_handshake(HelloRequest, Version), + Hs0 = tls_handshake:init_handshake_history(), {BinMsg, ConnectionStates} = - ssl_record:encode_handshake(Frag, Version, ConnectionStates0), + tls_record:encode_handshake(Frag, Version, ConnectionStates0), Transport:send(Socket, BinMsg), {Record, State} = next_record(State0#state{connection_states = ConnectionStates, @@ -2400,7 +2949,7 @@ workaround_transport_delivery_problems(Socket, gen_tcp = Transport) -> %% data sent to the tcp port is really delivered to the %% peer application before tcp port is closed so that the peer will %% get the correct TLS alert message and not only a transport close. - inet:setopts(Socket, [{active, false}]), + ssl_socket:setopts(Transport, Socket, [{active, false}]), Transport:shutdown(Socket, write), %% Will return when other side has closed or after 30 s %% e.g. we do not want to hang if something goes wrong @@ -2423,14 +2972,14 @@ handle_trusted_certs_db(#state{cert_db_ref = Ref, ssl_options = #ssl_options{cacertfile = undefined}}) -> %% Certs provided as DER directly can not be shared %% with other connections and it is safe to delete them when the connection ends. - ssl_certificate_db:remove_trusted_certs(Ref, CertDb); + ssl_pkix_db:remove_trusted_certs(Ref, CertDb); handle_trusted_certs_db(#state{file_ref_db = undefined}) -> %% Something went wrong early (typically cacertfile does not exist) so there is nothing to handle ok; handle_trusted_certs_db(#state{cert_db_ref = Ref, file_ref_db = RefDb, ssl_options = #ssl_options{cacertfile = File}}) -> - case ssl_certificate_db:ref_count(Ref, RefDb, -1) of + case ssl_pkix_db:ref_count(Ref, RefDb, -1) of 0 -> ssl_manager:clean_cert_db(Ref, File); _ -> @@ -2438,16 +2987,16 @@ handle_trusted_certs_db(#state{cert_db_ref = Ref, end. get_current_connection_state_prf(CStates, Direction) -> - CS = ssl_record:current_connection_state(CStates, Direction), + CS = tls_record:current_connection_state(CStates, Direction), CS#connection_state.security_parameters#security_parameters.prf_algorithm. get_pending_connection_state_prf(CStates, Direction) -> - CS = ssl_record:pending_connection_state(CStates, Direction), + CS = tls_record:pending_connection_state(CStates, Direction), CS#connection_state.security_parameters#security_parameters.prf_algorithm. -connection_hash_algo({HashAlgo, _}, _State) -> - HashAlgo; -connection_hash_algo(_, #state{hashsign_algorithm = {HashAlgo, _}}) -> - HashAlgo. +connection_hashsign(HashSign = {_, _}, _State) -> + HashSign; +connection_hashsign(_, #state{hashsign_algorithm = HashSign}) -> + HashSign. %% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms %% If the client does not send the signature_algorithms extension, the @@ -2467,33 +3016,49 @@ default_hashsign(_Version = {Major, Minor}, KeyExchange) when Major == 3 andalso Minor >= 3 andalso (KeyExchange == rsa orelse KeyExchange == dhe_rsa orelse - KeyExchange == dh_rsa) -> + KeyExchange == dh_rsa orelse + KeyExchange == ecdhe_rsa orelse + KeyExchange == srp_rsa) -> {sha, rsa}; default_hashsign(_Version, KeyExchange) when KeyExchange == rsa; KeyExchange == dhe_rsa; - KeyExchange == dh_rsa -> + KeyExchange == dh_rsa; + KeyExchange == ecdhe_rsa; + KeyExchange == srp_rsa -> {md5sha, rsa}; default_hashsign(_Version, KeyExchange) + when KeyExchange == ecdhe_ecdsa; + KeyExchange == ecdh_ecdsa; + KeyExchange == ecdh_rsa -> + {sha, ecdsa}; +default_hashsign(_Version, KeyExchange) when KeyExchange == dhe_dss; - KeyExchange == dh_dss -> + KeyExchange == dh_dss; + KeyExchange == srp_dss -> {sha, dsa}; default_hashsign(_Version, KeyExchange) - when KeyExchange == dh_anon -> + when KeyExchange == dh_anon; + KeyExchange == ecdh_anon; + KeyExchange == psk; + KeyExchange == dhe_psk; + KeyExchange == rsa_psk; + KeyExchange == srp_anon -> {null, anon}. start_or_recv_cancel_timer(infinity, _RecvFrom) -> undefined; start_or_recv_cancel_timer(Timeout, RecvFrom) -> - erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}). + erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}). cancel_timer(undefined) -> ok; cancel_timer(Timer) -> - erlang:cancel_timer(Timer). + erlang:cancel_timer(Timer), + ok. handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport} = State) -> - inet:setopts(Socket, [{active, false}]), + ssl_socket:setopts(Transport, Socket, [{active, false}]), case Transport:recv(Socket, 0, 0) of {error, closed} -> ok; @@ -2509,3 +3074,8 @@ handle_close_alert(Data, StateName, State0) -> _ -> ok end. + +select_curve(#state{client_ecc = {[Curve|_], _}}) -> + {namedCurve, Curve}; +select_curve(_) -> + {namedCurve, ?secp256k1}. diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/tls_handshake.erl index bb26302fff..51fd2e1dc9 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -21,30 +21,33 @@ %% Purpose: Help funtions for handling the SSL-handshake protocol %%---------------------------------------------------------------------- --module(ssl_handshake). +-module(tls_handshake). --include("ssl_handshake.hrl"). --include("ssl_record.hrl"). +-include("tls_handshake.hrl"). +-include("tls_record.hrl"). -include("ssl_cipher.hrl"). -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). +-include("ssl_srp.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([master_secret/4, client_hello/8, server_hello/4, hello/4, +-export([master_secret/4, client_hello/8, server_hello/7, hello/4, hello_request/0, certify/7, certificate/4, - client_certificate_verify/6, certificate_verify/6, + client_certificate_verify/6, certificate_verify/6, verify_signature/5, certificate_request/3, key_exchange/3, server_key_exchange_hash/2, finished/5, verify_connection/6, get_tls_handshake/3, - decode_client_key/3, server_hello_done/0, + decode_client_key/3, decode_server_key/3, server_hello_done/0, encode_handshake/2, init_handshake_history/0, update_handshake_history/2, - decrypt_premaster_secret/2, prf/5]). + decrypt_premaster_secret/2, prf/5, next_protocol/1]). -export([dec_hello_extensions/2]). -type tls_handshake() :: #client_hello{} | #server_hello{} | #server_hello_done{} | #certificate{} | #certificate_request{} | #client_key_exchange{} | #finished{} | #certificate_verify{} | - #hello_request{}. + #hello_request{} | #next_protocol{}. + +-define(NAMED_CURVE_TYPE, 3). %%==================================================================== %% Internal application API @@ -61,35 +64,52 @@ client_hello(Host, Port, ConnectionStates, ciphers = UserSuites } = SslOpts, Cache, CacheCb, Renegotiation, OwnCert) -> - - Fun = fun(Version) -> - ssl_record:protocol_version(Version) - end, - Version = ssl_record:highest_protocol_version(lists:map(Fun, Versions)), - Pending = ssl_record:pending_connection_state(ConnectionStates, read), + Version = tls_record:highest_protocol_version(Versions), + Pending = tls_record:pending_connection_state(ConnectionStates, read), SecParams = Pending#connection_state.security_parameters, Ciphers = available_suites(UserSuites, Version), + SRP = srp_user(SslOpts), + {EcPointFormats, EllipticCurves} = default_ecc_extensions(Version), Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), #client_hello{session_id = Id, client_version = Version, cipher_suites = cipher_suites(Ciphers, Renegotiation), - compression_methods = ssl_record:compressions(), + compression_methods = tls_record:compressions(), random = SecParams#security_parameters.client_random, + renegotiation_info = renegotiation_info(client, ConnectionStates, Renegotiation), - hash_signs = default_hash_signs() + srp = SRP, + hash_signs = default_hash_signs(), + ec_point_formats = EcPointFormats, + elliptic_curves = EllipticCurves, + next_protocol_negotiation = + encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector, Renegotiation) }. +encode_protocol(Protocol, Acc) -> + Len = byte_size(Protocol), + <<Acc/binary, ?BYTE(Len), Protocol/binary>>. + +encode_protocols_advertised_on_server(undefined) -> + undefined; + +encode_protocols_advertised_on_server(Protocols) -> + #next_protocol_negotiation{extension_data = lists:foldl(fun encode_protocol/2, <<>>, Protocols)}. + %%-------------------------------------------------------------------- -spec server_hello(session_id(), tls_version(), #connection_states{}, - boolean()) -> #server_hello{}. + boolean(), [binary()] | undefined, + #ec_point_formats{} | undefined, + #elliptic_curves{} | undefined) -> #server_hello{}. %% %% Description: Creates a server hello message. %%-------------------------------------------------------------------- -server_hello(SessionId, Version, ConnectionStates, Renegotiation) -> - Pending = ssl_record:pending_connection_state(ConnectionStates, read), +server_hello(SessionId, Version, ConnectionStates, Renegotiation, + ProtocolsAdvertisedOnServer, EcPointFormats, EllipticCurves) -> + Pending = tls_record:pending_connection_state(ConnectionStates, read), SecParams = Pending#connection_state.security_parameters, #server_hello{server_version = Version, cipher_suite = SecParams#security_parameters.cipher_suite, @@ -98,7 +118,10 @@ server_hello(SessionId, Version, ConnectionStates, Renegotiation) -> random = SecParams#security_parameters.server_random, session_id = SessionId, renegotiation_info = - renegotiation_info(server, ConnectionStates, Renegotiation) + renegotiation_info(server, ConnectionStates, Renegotiation), + ec_point_formats = EcPointFormats, + elliptic_curves = EllipticCurves, + next_protocol_negotiation = encode_protocols_advertised_on_server(ProtocolsAdvertisedOnServer) }. %%-------------------------------------------------------------------- @@ -113,21 +136,24 @@ hello_request() -> %%-------------------------------------------------------------------- -spec hello(#server_hello{} | #client_hello{}, #ssl_options{}, #connection_states{} | {inet:port_number(), #session{}, db_handle(), - atom(), #connection_states{}, binary()}, - boolean()) -> {tls_version(), session_id(), #connection_states{}}| - {tls_version(), {resumed | new, #session{}}, - #connection_states{}} | #alert{}. + atom(), #connection_states{}, binary()}, + boolean()) -> + {tls_version(), session_id(), #connection_states{}, binary() | undefined}| + {tls_version(), {resumed | new, #session{}}, #connection_states{}, [binary()] | undefined, + [oid()] | undefined, [oid()] | undefined} | + #alert{}. %% %% Description: Handles a recieved hello message %%-------------------------------------------------------------------- hello(#server_hello{cipher_suite = CipherSuite, server_version = Version, compression_method = Compression, random = Random, session_id = SessionId, renegotiation_info = Info, - hash_signs = _HashSigns}, - #ssl_options{secure_renegotiate = SecureRenegotation}, + hash_signs = _HashSigns} = Hello, + #ssl_options{secure_renegotiate = SecureRenegotation, next_protocol_selector = NextProtocolSelector, + versions = SupportedVersions}, ConnectionStates0, Renegotiation) -> -%%TODO: select hash and signature algorigthm - case ssl_record:is_acceptable_version(Version) of + %%TODO: select hash and signature algorigthm + case tls_record:is_acceptable_version(Version, SupportedVersions) of true -> case handle_renegotiation_info(client, Info, ConnectionStates0, Renegotiation, SecureRenegotation, []) of @@ -135,7 +161,12 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version, ConnectionStates = hello_pending_connection_states(client, Version, CipherSuite, Random, Compression, ConnectionStates1), - {Version, SessionId, ConnectionStates}; + case handle_next_protocol(Hello, NextProtocolSelector, Renegotiation) of + #alert{} = Alert -> + Alert; + Protocol -> + {Version, SessionId, ConnectionStates, Protocol} + end; #alert{} = Alert -> Alert end; @@ -143,38 +174,27 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version, ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) end; -hello(#client_hello{client_version = ClientVersion, random = Random, - cipher_suites = CipherSuites, - renegotiation_info = Info, - hash_signs = _HashSigns} = Hello, - #ssl_options{versions = Versions, - secure_renegotiate = SecureRenegotation} = SslOpts, +hello(#client_hello{client_version = ClientVersion} = Hello, + #ssl_options{versions = Versions} = SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) -> -%% TODO: select hash and signature algorithm + %% TODO: select hash and signature algorithm Version = select_version(ClientVersion, Versions), - case ssl_record:is_acceptable_version(Version) of + case tls_record:is_acceptable_version(Version, Versions) of true -> - {Type, #session{cipher_suite = CipherSuite, - compression_method = Compression} = Session} + %% TODO: need to take supported Curves into Account when selecting the CipherSuite.... + %% if whe have an ECDSA cert with an unsupported curve, we need to drop ECDSA ciphers + {Type, #session{cipher_suite = CipherSuite} = Session1} = select_session(Hello, Port, Session0, Version, SslOpts, Cache, CacheCb, Cert), case CipherSuite of no_suite -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY); _ -> - case handle_renegotiation_info(server, Info, ConnectionStates0, - Renegotiation, SecureRenegotation, - CipherSuites) of - {ok, ConnectionStates1} -> - ConnectionStates = - hello_pending_connection_states(server, - Version, - CipherSuite, - Random, - Compression, - ConnectionStates1), - {Version, {Type, Session}, ConnectionStates}; - #alert{} = Alert -> + try handle_hello_extensions(Hello, Version, SslOpts, Session1, ConnectionStates0, Renegotiation) of + {Session, ConnectionStates, ProtocolsToAdvertise, ECPointFormats, EllipticCurves} -> + {Version, {Type, Session}, ConnectionStates, + ProtocolsToAdvertise, ECPointFormats, EllipticCurves} + catch throw:Alert -> Alert end end; @@ -296,25 +316,37 @@ client_certificate_verify(OwnCert, MasterSecret, Version, %% %% Description: Checks that the certificate_verify message is valid. %%-------------------------------------------------------------------- -certificate_verify(Signature, {?'rsaEncryption', PublicKey, _}, Version, - {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) -> - Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), - case certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, Version) of +certificate_verify(Signature, PublicKeyInfo, Version, + HashSign = {HashAlgo, _}, MasterSecret, {_, Handshake}) -> + Hash = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), + case verify_signature(Version, Hash, HashSign, Signature, PublicKeyInfo) of true -> valid; _ -> - ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) - end; -certificate_verify(Signature, {?'id-dsa', PublicKey, PublicKeyParams}, Version, - {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) -> - Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), - case public_key:verify({digest, Hashes}, sha, Signature, {PublicKey, PublicKeyParams}) of - true -> - valid; - false -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) end. +%%-------------------------------------------------------------------- +-spec verify_signature(tls_version(), binary(), {term(), term()}, binary(), + public_key_info()) -> true | false. +%% +%% Description: Checks that a public_key signature is valid. +%%-------------------------------------------------------------------- +verify_signature(_Version, _Hash, {_HashAlgo, anon}, _Signature, _) -> + true; +verify_signature({3, Minor}, Hash, {HashAlgo, rsa}, Signature, {?rsaEncryption, PubKey, _PubKeyParams}) + when Minor >= 3 -> + public_key:verify({digest, Hash}, HashAlgo, Signature, PubKey); +verify_signature(_Version, Hash, _HashAlgo, Signature, {?rsaEncryption, PubKey, _PubKeyParams}) -> + case public_key:decrypt_public(Signature, PubKey, + [{rsa_pad, rsa_pkcs1_padding}]) of + Hash -> true; + _ -> false + end; +verify_signature(_Version, Hash, {HashAlgo, dsa}, Signature, {?'id-dsa', PublicKey, PublicKeyParams}) -> + public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams}); +verify_signature(_Version, Hash, {HashAlgo, ecdsa}, Signature, {?'id-ecPublicKey', PublicKey, PublicKeyParams}) -> + public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams}). %%-------------------------------------------------------------------- -spec certificate_request(#connection_states{}, db_handle(), certdb_ref()) -> @@ -325,7 +357,7 @@ certificate_verify(Signature, {?'id-dsa', PublicKey, PublicKeyParams}, Version, certificate_request(ConnectionStates, CertDbHandle, CertDbRef) -> #connection_state{security_parameters = #security_parameters{cipher_suite = CipherSuite}} = - ssl_record:pending_connection_state(ConnectionStates, read), + tls_record:pending_connection_state(ConnectionStates, read), Types = certificate_types(CipherSuite), HashSigns = default_hash_signs(), Authorities = certificate_authorities(CertDbHandle, CertDbRef), @@ -340,6 +372,11 @@ certificate_request(ConnectionStates, CertDbHandle, CertDbRef) -> {premaster_secret, binary(), public_key_info()} | {dh, binary()} | {dh, {binary(), binary()}, #'DHParameter'{}, {HashAlgo::atom(), SignAlgo::atom()}, + binary(), binary(), private_key()} | + {ecdh, #'ECPrivateKey'{}} | + {psk, binary()} | + {dhe_psk, binary(), binary()} | + {srp, {binary(), binary()}, #srp_user{}, {HashAlgo::atom(), SignAlgo::atom()}, binary(), binary(), private_key()}) -> #client_key_exchange{} | #server_key_exchange{}. %% @@ -350,39 +387,105 @@ key_exchange(client, _Version, {premaster_secret, Secret, {_, PublicKey, _}}) -> encrypted_premaster_secret(Secret, PublicKey), #client_key_exchange{exchange_keys = EncPremasterSecret}; -key_exchange(client, _Version, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) -> +key_exchange(client, _Version, {dh, PublicKey}) -> #client_key_exchange{ exchange_keys = #client_diffie_hellman_public{ dh_public = PublicKey} }; -key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, - #'DHParameter'{prime = P, base = G}, - {HashAlgo, SignAlgo}, ClientRandom, ServerRandom, PrivateKey}) -> - <<?UINT32(_), PBin/binary>> = crypto:mpint(P), - <<?UINT32(_), GBin/binary>> = crypto:mpint(G), - PLen = byte_size(PBin), - GLen = byte_size(GBin), - YLen = byte_size(PublicKey), - ServerDHParams = #server_dh_params{dh_p = PBin, - dh_g = GBin, dh_y = PublicKey}, +key_exchange(client, _Version, {ecdh, #'ECPrivateKey'{publicKey = {0, ECPublicKey}}}) -> + #client_key_exchange{ + exchange_keys = #client_ec_diffie_hellman_public{ + dh_public = ECPublicKey} + }; + +key_exchange(client, _Version, {psk, Identity}) -> + #client_key_exchange{ + exchange_keys = #client_psk_identity{ + identity = Identity} + }; + +key_exchange(client, _Version, {dhe_psk, Identity, PublicKey}) -> + #client_key_exchange{ + exchange_keys = #client_dhe_psk_identity{ + identity = Identity, + dh_public = PublicKey} + }; + +key_exchange(client, _Version, {psk_premaster_secret, PskIdentity, Secret, {_, PublicKey, _}}) -> + EncPremasterSecret = + encrypted_premaster_secret(Secret, PublicKey), + #client_key_exchange{ + exchange_keys = #client_rsa_psk_identity{ + identity = PskIdentity, + exchange_keys = EncPremasterSecret}}; + +key_exchange(client, _Version, {srp, PublicKey}) -> + #client_key_exchange{ + exchange_keys = #client_srp_public{ + srp_a = PublicKey} + }; +key_exchange(server, Version, {dh, {PublicKey, _}, + #'DHParameter'{prime = P, base = G}, + HashSign, ClientRandom, ServerRandom, PrivateKey}) -> + ServerDHParams = #server_dh_params{dh_p = int_to_bin(P), + dh_g = int_to_bin(G), dh_y = PublicKey}, + enc_server_key_exchange(Version, ServerDHParams, HashSign, + ClientRandom, ServerRandom, PrivateKey); + +key_exchange(server, Version, {ecdh, #'ECPrivateKey'{publicKey = {0, ECPublicKey}, + parameters = ECCurve}, HashSign, ClientRandom, ServerRandom, + PrivateKey}) -> + ServerECParams = #server_ecdh_params{curve = ECCurve, public = ECPublicKey}, + enc_server_key_exchange(Version, ServerECParams, HashSign, + ClientRandom, ServerRandom, PrivateKey); + +key_exchange(server, Version, {psk, PskIdentityHint, + HashSign, ClientRandom, ServerRandom, PrivateKey}) -> + ServerPSKParams = #server_psk_params{hint = PskIdentityHint}, + enc_server_key_exchange(Version, ServerPSKParams, HashSign, + ClientRandom, ServerRandom, PrivateKey); + +key_exchange(server, Version, {dhe_psk, PskIdentityHint, {PublicKey, _}, + #'DHParameter'{prime = P, base = G}, + HashSign, ClientRandom, ServerRandom, PrivateKey}) -> + ServerEDHPSKParams = #server_dhe_psk_params{ + hint = PskIdentityHint, + dh_params = #server_dh_params{dh_p = int_to_bin(P), + dh_g = int_to_bin(G), dh_y = PublicKey} + }, + enc_server_key_exchange(Version, ServerEDHPSKParams, + HashSign, ClientRandom, ServerRandom, PrivateKey); + +key_exchange(server, Version, {srp, {PublicKey, _}, + #srp_user{generator = Generator, prime = Prime, + salt = Salt}, + HashSign, ClientRandom, ServerRandom, PrivateKey}) -> + ServerSRPParams = #server_srp_params{srp_n = Prime, srp_g = Generator, + srp_s = Salt, srp_b = PublicKey}, + enc_server_key_exchange(Version, ServerSRPParams, HashSign, + ClientRandom, ServerRandom, PrivateKey). + +enc_server_key_exchange(Version, Params, {HashAlgo, SignAlgo}, + ClientRandom, ServerRandom, PrivateKey) -> + EncParams = enc_server_key(Params), case HashAlgo of null -> - #server_key_exchange{params = ServerDHParams, - signed_params = <<>>, - hashsign = {null, anon}}; + #server_key_params{params = Params, + params_bin = EncParams, + hashsign = {null, anon}, + signature = <<>>}; _ -> Hash = server_key_exchange_hash(HashAlgo, <<ClientRandom/binary, - ServerRandom/binary, - ?UINT16(PLen), PBin/binary, - ?UINT16(GLen), GBin/binary, - ?UINT16(YLen), PublicKey/binary>>), - Signed = digitally_signed(Version, Hash, HashAlgo, PrivateKey), - #server_key_exchange{params = ServerDHParams, - signed_params = Signed, - hashsign = {HashAlgo, SignAlgo}} + ServerRandom/binary, + EncParams/binary>>), + Signature = digitally_signed(Version, Hash, HashAlgo, PrivateKey), + #server_key_params{params = Params, + params_bin = EncParams, + hashsign = {HashAlgo, SignAlgo}, + signature = Signature} end. %%-------------------------------------------------------------------- @@ -396,7 +499,7 @@ key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, master_secret(Version, #session{master_secret = Mastersecret}, ConnectionStates, Role) -> ConnectionState = - ssl_record:pending_connection_state(ConnectionStates, read), + tls_record:pending_connection_state(ConnectionStates, read), SecParams = ConnectionState#connection_state.security_parameters, try master_secret(Version, Mastersecret, SecParams, ConnectionStates, Role) @@ -410,7 +513,7 @@ master_secret(Version, #session{master_secret = Mastersecret}, master_secret(Version, PremasterSecret, ConnectionStates, Role) -> ConnectionState = - ssl_record:pending_connection_state(ConnectionStates, read), + tls_record:pending_connection_state(ConnectionStates, read), SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{prf_algorithm = PrfAlgo, client_random = ClientRandom, @@ -427,6 +530,11 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) end. +-spec next_protocol(binary()) -> #next_protocol{}. + +next_protocol(SelectedProtocol) -> + #next_protocol{selected_protocol = SelectedProtocol}. + %%-------------------------------------------------------------------- -spec finished(tls_version(), client | server, integer(), binary(), tls_handshake_history()) -> #finished{}. @@ -486,7 +594,13 @@ get_tls_handshake(Version, Data, Buffer) -> %%-------------------------------------------------------------------- -spec decode_client_key(binary(), key_algo(), tls_version()) -> - #encrypted_premaster_secret{} | #client_diffie_hellman_public{}. + #encrypted_premaster_secret{} + | #client_diffie_hellman_public{} + | #client_ec_diffie_hellman_public{} + | #client_psk_identity{} + | #client_dhe_psk_identity{} + | #client_rsa_psk_identity{} + | #client_srp_public{}. %% %% Description: Decode client_key data and return appropriate type %%-------------------------------------------------------------------- @@ -494,6 +608,15 @@ decode_client_key(ClientKey, Type, Version) -> dec_client_key(ClientKey, key_exchange_alg(Type), Version). %%-------------------------------------------------------------------- +-spec decode_server_key(binary(), key_algo(), tls_version()) -> + #server_key_params{}. +%% +%% Description: Decode server_key data and return appropriate type +%%-------------------------------------------------------------------- +decode_server_key(ServerKey, Type, Version) -> + dec_server_key(ServerKey, key_exchange_alg(Type), Version). + +%%-------------------------------------------------------------------- -spec init_handshake_history() -> tls_handshake_history(). %% @@ -544,8 +667,8 @@ decrypt_premaster_secret(Secret, RSAPrivateKey) -> %% Description: Calculate server key exchange hash %%-------------------------------------------------------------------- server_key_exchange_hash(md5sha, Value) -> - MD5 = crypto:md5(Value), - SHA = crypto:sha(Value), + MD5 = crypto:hash(md5, Value), + SHA = crypto:hash(sha, Value), <<MD5/binary, SHA/binary>>; server_key_exchange_hash(Hash, Value) -> @@ -629,10 +752,15 @@ cipher_suites(Suites, false) -> cipher_suites(Suites, true) -> Suites. +srp_user(#ssl_options{srp_identity = {UserName, _}}) -> + #srp{username = UserName}; +srp_user(_) -> + undefined. + renegotiation_info(client, _, false) -> #renegotiation_info{renegotiated_connection = undefined}; renegotiation_info(server, ConnectionStates, false) -> - CS = ssl_record:current_connection_state(ConnectionStates, read), + CS = tls_record:current_connection_state(ConnectionStates, read), case CS#connection_state.secure_renegotiation of true -> #renegotiation_info{renegotiated_connection = ?byte(0)}; @@ -640,7 +768,7 @@ renegotiation_info(server, ConnectionStates, false) -> #renegotiation_info{renegotiated_connection = undefined} end; renegotiation_info(client, ConnectionStates, true) -> - CS = ssl_record:current_connection_state(ConnectionStates, read), + CS = tls_record:current_connection_state(ConnectionStates, read), case CS#connection_state.secure_renegotiation of true -> Data = CS#connection_state.client_verify_data, @@ -650,7 +778,7 @@ renegotiation_info(client, ConnectionStates, true) -> end; renegotiation_info(server, ConnectionStates, true) -> - CS = ssl_record:current_connection_state(ConnectionStates, read), + CS = tls_record:current_connection_state(ConnectionStates, read), case CS#connection_state.secure_renegotiation of true -> CData = CS#connection_state.client_verify_data, @@ -660,24 +788,107 @@ renegotiation_info(server, ConnectionStates, true) -> #renegotiation_info{renegotiated_connection = undefined} end. +decode_next_protocols({next_protocol_negotiation, Protocols}) -> + decode_next_protocols(Protocols, []). +decode_next_protocols(<<>>, Acc) -> + lists:reverse(Acc); +decode_next_protocols(<<?BYTE(Len), Protocol:Len/binary, Rest/binary>>, Acc) -> + case Len of + 0 -> + {error, invalid_next_protocols}; + _ -> + decode_next_protocols(Rest, [Protocol|Acc]) + end; +decode_next_protocols(_Bytes, _Acc) -> + {error, invalid_next_protocols}. + +next_protocol_extension_allowed(NextProtocolSelector, Renegotiating) -> + NextProtocolSelector =/= undefined andalso not Renegotiating. + +handle_next_protocol_on_server(#client_hello{next_protocol_negotiation = undefined}, _Renegotiation, _SslOpts) -> + undefined; + +handle_next_protocol_on_server(#client_hello{next_protocol_negotiation = {next_protocol_negotiation, <<>>}}, + false, #ssl_options{next_protocols_advertised = Protocols}) -> + Protocols; + +handle_next_protocol_on_server(_Hello, _Renegotiation, _SSLOpts) -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE). % unexpected next protocol extension + +handle_next_protocol(#server_hello{next_protocol_negotiation = undefined}, + _NextProtocolSelector, _Renegotiating) -> + undefined; + +handle_next_protocol(#server_hello{next_protocol_negotiation = Protocols}, + NextProtocolSelector, Renegotiating) -> + + case next_protocol_extension_allowed(NextProtocolSelector, Renegotiating) of + true -> + select_next_protocol(decode_next_protocols(Protocols), NextProtocolSelector); + false -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) % unexpected next protocol extension + end. + +select_next_protocol({error, _Reason}, _NextProtocolSelector) -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE); +select_next_protocol(Protocols, NextProtocolSelector) -> + case NextProtocolSelector(Protocols) of + ?NO_PROTOCOL -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE); + Protocol when is_binary(Protocol) -> + Protocol + end. + +default_ecc_extensions(Version) -> + CryptoSupport = proplists:get_value(public_keys, crypto:supports()), + case proplists:get_bool(ecdh, CryptoSupport) of + true -> + EcPointFormats = #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]}, + EllipticCurves = #elliptic_curves{elliptic_curve_list = ssl_tls1:ecc_curves(Version)}, + {EcPointFormats, EllipticCurves}; + _ -> + {undefined, undefined} + end. + +handle_ecc_extensions(Version, EcPointFormats0, EllipticCurves0) -> + CryptoSupport = proplists:get_value(public_keys, crypto:supports()), + case proplists:get_bool(ecdh, CryptoSupport) of + true -> + EcPointFormats1 = handle_ecc_point_fmt_extension(EcPointFormats0), + EllipticCurves1 = handle_ecc_curves_extension(Version, EllipticCurves0), + {EcPointFormats1, EllipticCurves1}; + _ -> + {undefined, undefined} + end. + +handle_ecc_point_fmt_extension(undefined) -> + undefined; +handle_ecc_point_fmt_extension(_) -> + #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]}. + +handle_ecc_curves_extension(_Version, undefined) -> + undefined; +handle_ecc_curves_extension(Version, _) -> + #elliptic_curves{elliptic_curve_list = ssl_tls1:ecc_curves(Version)}. + handle_renegotiation_info(_, #renegotiation_info{renegotiated_connection = ?byte(0)}, ConnectionStates, false, _, _) -> - {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)}; + {ok, tls_record:set_renegotiation_flag(true, ConnectionStates)}; handle_renegotiation_info(server, undefined, ConnectionStates, _, _, CipherSuites) -> case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of true -> - {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)}; + {ok, tls_record:set_renegotiation_flag(true, ConnectionStates)}; false -> - {ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)} + {ok, tls_record:set_renegotiation_flag(false, ConnectionStates)} end; handle_renegotiation_info(_, undefined, ConnectionStates, false, _, _) -> - {ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)}; + {ok, tls_record:set_renegotiation_flag(false, ConnectionStates)}; handle_renegotiation_info(client, #renegotiation_info{renegotiated_connection = ClientServerVerify}, ConnectionStates, true, _, _) -> - CS = ssl_record:current_connection_state(ConnectionStates, read), + CS = tls_record:current_connection_state(ConnectionStates, read), CData = CS#connection_state.client_verify_data, SData = CS#connection_state.server_verify_data, case <<CData/binary, SData/binary>> == ClientServerVerify of @@ -693,7 +904,7 @@ handle_renegotiation_info(server, #renegotiation_info{renegotiated_connection = true -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE); false -> - CS = ssl_record:current_connection_state(ConnectionStates, read), + CS = tls_record:current_connection_state(ConnectionStates, read), Data = CS#connection_state.client_verify_data, case Data == ClientVerify of true -> @@ -715,7 +926,7 @@ handle_renegotiation_info(server, undefined, ConnectionStates, true, SecureReneg end. handle_renegotiation_info(ConnectionStates, SecureRenegotation) -> - CS = ssl_record:current_connection_state(ConnectionStates, read), + CS = tls_record:current_connection_state(ConnectionStates, read), case {SecureRenegotation, CS#connection_state.secure_renegotiation} of {_, true} -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE); @@ -732,9 +943,9 @@ handle_renegotiation_info(ConnectionStates, SecureRenegotation) -> hello_pending_connection_states(Role, Version, CipherSuite, Random, Compression, ConnectionStates) -> ReadState = - ssl_record:pending_connection_state(ConnectionStates, read), + tls_record:pending_connection_state(ConnectionStates, read), WriteState = - ssl_record:pending_connection_state(ConnectionStates, write), + tls_record:pending_connection_state(ConnectionStates, write), NewReadSecParams = hello_security_parameters(Role, Version, ReadState, CipherSuite, @@ -744,7 +955,7 @@ hello_pending_connection_states(Role, Version, CipherSuite, Random, Compression, hello_security_parameters(Role, Version, WriteState, CipherSuite, Random, Compression), - ssl_record:update_security_params(NewReadSecParams, + tls_record:update_security_params(NewReadSecParams, NewWriteSecParams, ConnectionStates). @@ -767,12 +978,8 @@ hello_security_parameters(server, Version, ConnectionState, CipherSuite, Random, }. select_version(ClientVersion, Versions) -> - Fun = fun(Version) -> - ssl_record:protocol_version(Version) - end, - ServerVersion = ssl_record:highest_protocol_version(lists:map(Fun, - Versions)), - ssl_record:lowest_protocol_version(ClientVersion, ServerVersion). + ServerVersion = tls_record:highest_protocol_version(Versions), + tls_record:lowest_protocol_version(ClientVersion, ServerVersion). select_cipher_suite([], _) -> no_suite; @@ -804,29 +1011,33 @@ master_secret(Version, MasterSecret, #security_parameters{ setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, KML, EKML, IVS), - ConnStates1 = ssl_record:set_master_secret(MasterSecret, ConnectionStates), + ConnStates1 = tls_record:set_master_secret(MasterSecret, ConnectionStates), ConnStates2 = - ssl_record:set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, + tls_record:set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, Role, ConnStates1), ClientCipherState = #cipher_state{iv = ClientIV, key = ClientWriteKey}, ServerCipherState = #cipher_state{iv = ServerIV, key = ServerWriteKey}, {MasterSecret, - ssl_record:set_pending_cipher_state(ConnStates2, ClientCipherState, + tls_record:set_pending_cipher_state(ConnStates2, ClientCipherState, ServerCipherState, Role)}. -dec_hs(_Version, ?HELLO_REQUEST, <<>>) -> +dec_hs(_, ?NEXT_PROTOCOL, <<?BYTE(SelectedProtocolLength), SelectedProtocol:SelectedProtocolLength/binary, + ?BYTE(PaddingLength), _Padding:PaddingLength/binary>>) -> + #next_protocol{selected_protocol = SelectedProtocol}; + +dec_hs(_, ?HELLO_REQUEST, <<>>) -> #hello_request{}; %% Client hello v2. %% The server must be able to receive such messages, from clients that %% are willing to use ssl v3 or higher, but have ssl v2 compatibility. dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), - ?UINT16(CSLength), ?UINT16(0), - ?UINT16(CDLength), - CipherSuites:CSLength/binary, - ChallengeData:CDLength/binary>>) -> + ?UINT16(CSLength), ?UINT16(0), + ?UINT16(CDLength), + CipherSuites:CSLength/binary, + ChallengeData:CDLength/binary>>) -> #client_hello{client_version = {Major, Minor}, random = ssl_ssl2:client_random(ChallengeData, CDLength), session_id = 0, @@ -839,20 +1050,27 @@ dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?UINT16(Cs_length), CipherSuites:Cs_length/binary, ?BYTE(Cm_length), Comp_methods:Cm_length/binary, Extensions/binary>>) -> - HelloExtensions = dec_hello_extensions(Extensions), - RenegotiationInfo = proplists:get_value(renegotiation_info, HelloExtensions, - undefined), - HashSigns = proplists:get_value(hash_signs, HelloExtensions, - undefined), + + DecodedExtensions = dec_hello_extensions(Extensions), + RenegotiationInfo = proplists:get_value(renegotiation_info, DecodedExtensions, undefined), + SRP = proplists:get_value(srp, DecodedExtensions, undefined), + HashSigns = proplists:get_value(hash_signs, DecodedExtensions, undefined), + EllipticCurves = proplists:get_value(elliptic_curves, DecodedExtensions, + undefined), + NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, DecodedExtensions, undefined), + #client_hello{ - client_version = {Major,Minor}, - random = Random, - session_id = Session_ID, - cipher_suites = from_2bytes(CipherSuites), - compression_methods = Comp_methods, - renegotiation_info = RenegotiationInfo, - hash_signs = HashSigns - }; + client_version = {Major,Minor}, + random = Random, + session_id = Session_ID, + cipher_suites = from_2bytes(CipherSuites), + compression_methods = Comp_methods, + renegotiation_info = RenegotiationInfo, + srp = SRP, + hash_signs = HashSigns, + elliptic_curves = EllipticCurves, + next_protocol_negotiation = NextProtocolNegotiation + }; dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, @@ -864,11 +1082,12 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, cipher_suite = Cipher_suite, compression_method = Comp_method, renegotiation_info = undefined, - hash_signs = undefined}; + hash_signs = undefined, + elliptic_curves = undefined}; dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, - Cipher_suite:2/binary, ?BYTE(Comp_method), + Cipher_suite:2/binary, ?BYTE(Comp_method), ?UINT16(ExtLen), Extensions:ExtLen/binary>>) -> HelloExtensions = dec_hello_extensions(Extensions, []), @@ -876,6 +1095,10 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, undefined), HashSigns = proplists:get_value(hash_signs, HelloExtensions, undefined), + EllipticCurves = proplists:get_value(elliptic_curves, HelloExtensions, + undefined), + NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, HelloExtensions, undefined), + #server_hello{ server_version = {Major,Minor}, random = Random, @@ -883,34 +1106,13 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, cipher_suite = Cipher_suite, compression_method = Comp_method, renegotiation_info = RenegotiationInfo, - hash_signs = HashSigns}; + hash_signs = HashSigns, + elliptic_curves = EllipticCurves, + next_protocol_negotiation = NextProtocolNegotiation}; dec_hs(_Version, ?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) -> #certificate{asn1_certificates = certs_to_list(ASN1Certs)}; - -dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, - ?UINT16(GLen), G:GLen/binary, - ?UINT16(YLen), Y:YLen/binary, - ?UINT16(0)>>) -> %% May happen if key_algorithm is dh_anon - #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, - dh_y = Y}, - signed_params = <<>>, hashsign = {null, anon}}; -dec_hs({Major, Minor}, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, - ?UINT16(GLen), G:GLen/binary, - ?UINT16(YLen), Y:YLen/binary, - ?BYTE(HashAlgo), ?BYTE(SignAlgo), - ?UINT16(Len), Sig:Len/binary>>) - when Major == 3, Minor >= 3 -> - #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, - dh_y = Y}, - signed_params = Sig, - hashsign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}}; -dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, - ?UINT16(GLen), G:GLen/binary, - ?UINT16(YLen), Y:YLen/binary, - ?UINT16(Len), Sig:Len/binary>>) -> - #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, - dh_y = Y}, - signed_params = Sig, hashsign = undefined}; +dec_hs(_Version, ?SERVER_KEY_EXCHANGE, Keys) -> + #server_key_exchange{exchange_keys = Keys}; dec_hs({Major, Minor}, ?CERTIFICATE_REQUEST, <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary, ?UINT16(HashSignsLen), HashSigns:HashSignsLen/binary, @@ -948,7 +1150,112 @@ dec_client_key(<<>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> throw(?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE)); dec_client_key(<<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> - #client_diffie_hellman_public{dh_public = DH_Y}. + #client_diffie_hellman_public{dh_public = DH_Y}; +dec_client_key(<<>>, ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, _) -> + throw(?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE)); +dec_client_key(<<?BYTE(DH_YLen), DH_Y:DH_YLen/binary>>, + ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, _) -> + #client_ec_diffie_hellman_public{dh_public = DH_Y}; +dec_client_key(<<?UINT16(Len), Id:Len/binary>>, + ?KEY_EXCHANGE_PSK, _) -> + #client_psk_identity{identity = Id}; +dec_client_key(<<?UINT16(Len), Id:Len/binary, + ?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>, + ?KEY_EXCHANGE_DHE_PSK, _) -> + #client_dhe_psk_identity{identity = Id, dh_public = DH_Y}; +dec_client_key(<<?UINT16(Len), Id:Len/binary, PKEPMS/binary>>, + ?KEY_EXCHANGE_RSA_PSK, {3, 0}) -> + #client_rsa_psk_identity{identity = Id, exchange_keys = #encrypted_premaster_secret{premaster_secret = PKEPMS}}; +dec_client_key(<<?UINT16(Len), Id:Len/binary, ?UINT16(_), PKEPMS/binary>>, + ?KEY_EXCHANGE_RSA_PSK, _) -> + #client_rsa_psk_identity{identity = Id, exchange_keys = #encrypted_premaster_secret{premaster_secret = PKEPMS}}; +dec_client_key(<<?UINT16(ALen), A:ALen/binary>>, + ?KEY_EXCHANGE_SRP, _) -> + #client_srp_public{srp_a = A}. + +dec_ske_params(Len, Keys, Version) -> + <<Params:Len/bytes, Signature/binary>> = Keys, + dec_ske_signature(Params, Signature, Version). + +dec_ske_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo), + ?UINT16(0)>>, {Major, Minor}) + when Major == 3, Minor >= 3 -> + HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}, + {Params, HashSign, <<>>}; +dec_ske_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo), + ?UINT16(Len), Signature:Len/binary>>, {Major, Minor}) + when Major == 3, Minor >= 3 -> + HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}, + {Params, HashSign, Signature}; +dec_ske_signature(Params, <<>>, _) -> + {Params, {null, anon}, <<>>}; +dec_ske_signature(Params, <<?UINT16(0)>>, _) -> + {Params, {null, anon}, <<>>}; +dec_ske_signature(Params, <<?UINT16(Len), Signature:Len/binary>>, _) -> + {Params, undefined, Signature}; +dec_ske_signature(_, _, _) -> + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)). + +dec_server_key(<<?UINT16(PLen), P:PLen/binary, + ?UINT16(GLen), G:GLen/binary, + ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct, + ?KEY_EXCHANGE_DIFFIE_HELLMAN, Version) -> + Params = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y}, + {BinMsg, HashSign, Signature} = dec_ske_params(PLen + GLen + YLen + 6, KeyStruct, Version), + #server_key_params{params = Params, + params_bin = BinMsg, + hashsign = HashSign, + signature = Signature}; +%% ECParameters with named_curve +%% TODO: explicit curve +dec_server_key(<<?BYTE(?NAMED_CURVE), ?UINT16(CurveID), + ?BYTE(PointLen), ECPoint:PointLen/binary, + _/binary>> = KeyStruct, + ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, Version) -> + Params = #server_ecdh_params{curve = {namedCurve, ssl_tls1:enum_to_oid(CurveID)}, + public = ECPoint}, + {BinMsg, HashSign, Signature} = dec_ske_params(PointLen + 4, KeyStruct, Version), + #server_key_params{params = Params, + params_bin = BinMsg, + hashsign = HashSign, + signature = Signature}; +dec_server_key(<<?UINT16(Len), PskIdentityHint:Len/binary>> = KeyStruct, + KeyExchange, Version) + when KeyExchange == ?KEY_EXCHANGE_PSK; KeyExchange == ?KEY_EXCHANGE_RSA_PSK -> + Params = #server_psk_params{ + hint = PskIdentityHint}, + {BinMsg, HashSign, Signature} = dec_ske_params(Len + 2, KeyStruct, Version), + #server_key_params{params = Params, + params_bin = BinMsg, + hashsign = HashSign, + signature = Signature}; +dec_server_key(<<?UINT16(Len), IdentityHint:Len/binary, + ?UINT16(PLen), P:PLen/binary, + ?UINT16(GLen), G:GLen/binary, + ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct, + ?KEY_EXCHANGE_DHE_PSK, Version) -> + DHParams = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y}, + Params = #server_dhe_psk_params{ + hint = IdentityHint, + dh_params = DHParams}, + {BinMsg, HashSign, Signature} = dec_ske_params(Len + PLen + GLen + YLen + 8, KeyStruct, Version), + #server_key_params{params = Params, + params_bin = BinMsg, + hashsign = HashSign, + signature = Signature}; +dec_server_key(<<?UINT16(NLen), N:NLen/binary, + ?UINT16(GLen), G:GLen/binary, + ?BYTE(SLen), S:SLen/binary, + ?UINT16(BLen), B:BLen/binary, _/binary>> = KeyStruct, + ?KEY_EXCHANGE_SRP, Version) -> + Params = #server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B}, + {BinMsg, HashSign, Signature} = dec_ske_params(NLen + GLen + SLen + BLen + 7, KeyStruct, Version), + #server_key_params{params = Params, + params_bin = BinMsg, + hashsign = HashSign, + signature = Signature}; +dec_server_key(_, _, _) -> + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)). dec_hello_extensions(<<>>) -> []; @@ -959,6 +1266,9 @@ dec_hello_extensions(_) -> dec_hello_extensions(<<>>, Acc) -> Acc; +dec_hello_extensions(<<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData:Len/binary, Rest/binary>>, Acc) -> + Prop = {next_protocol_negotiation, #next_protocol_negotiation{extension_data = ExtensionData}}, + dec_hello_extensions(Rest, [Prop | Acc]); dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binary, Rest/binary>>, Acc) -> RenegotiateInfo = case Len of 1 -> % Initial handshake @@ -971,6 +1281,11 @@ dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binar dec_hello_extensions(Rest, [{renegotiation_info, #renegotiation_info{renegotiated_connection = RenegotiateInfo}} | Acc]); +dec_hello_extensions(<<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), SRP:SRPLen/binary, Rest/binary>>, Acc) + when Len == SRPLen + 2 -> + dec_hello_extensions(Rest, [{srp, + #srp{username = SRP}} | Acc]); + dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), ExtData:Len/binary, Rest/binary>>, Acc) -> SignAlgoListLen = Len - 2, @@ -980,8 +1295,25 @@ dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), dec_hello_extensions(Rest, [{hash_signs, #hash_sign_algos{hash_sign_algos = HashSignAlgos}} | Acc]); +dec_hello_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Acc) -> + EllipticCurveListLen = Len - 2, + <<?UINT16(EllipticCurveListLen), EllipticCurveList/binary>> = ExtData, + EllipticCurves = [ssl_tls1:enum_to_oid(X) || <<X:16>> <= EllipticCurveList], + dec_hello_extensions(Rest, [{elliptic_curves, + #elliptic_curves{elliptic_curve_list = EllipticCurves}} | Acc]); + +dec_hello_extensions(<<?UINT16(?EC_POINT_FORMATS_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Acc) -> + ECPointFormatListLen = Len - 1, + <<?BYTE(ECPointFormatListLen), ECPointFormatList/binary>> = ExtData, + ECPointFormats = binary_to_list(ECPointFormatList), + dec_hello_extensions(Rest, [{ec_point_formats, + #ec_point_formats{ec_point_format_list = ECPointFormats}} | Acc]); + %% Ignore data following the ClientHello (i.e., %% extensions) if not understood. + dec_hello_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len/binary, Rest/binary>>, Acc) -> dec_hello_extensions(Rest, Acc); %% This theoretically should not happen if the protocol is followed, but if it does it is ignored. @@ -1014,6 +1346,11 @@ certs_from_list(ACList) -> <<?UINT24(CertLen), Cert/binary>> end || Cert <- ACList]). +enc_hs(#next_protocol{selected_protocol = SelectedProtocol}, _Version) -> + PaddingLength = 32 - ((byte_size(SelectedProtocol) + 2) rem 32), + + {?NEXT_PROTOCOL, <<?BYTE((byte_size(SelectedProtocol))), SelectedProtocol/binary, + ?BYTE(PaddingLength), 0:(PaddingLength * 8)>>}; enc_hs(#hello_request{}, _Version) -> {?HELLO_REQUEST, <<>>}; enc_hs(#client_hello{client_version = {Major, Minor}, @@ -1022,19 +1359,26 @@ enc_hs(#client_hello{client_version = {Major, Minor}, cipher_suites = CipherSuites, compression_methods = CompMethods, renegotiation_info = RenegotiationInfo, - hash_signs = HashSigns}, _Version) -> + srp = SRP, + hash_signs = HashSigns, + ec_point_formats = EcPointFormats, + elliptic_curves = EllipticCurves, + next_protocol_negotiation = NextProtocolNegotiation}, _Version) -> SIDLength = byte_size(SessionID), BinCompMethods = list_to_binary(CompMethods), CmLength = byte_size(BinCompMethods), BinCipherSuites = list_to_binary(CipherSuites), CsLength = byte_size(BinCipherSuites), - Extensions0 = hello_extensions(RenegotiationInfo), + Extensions0 = hello_extensions(RenegotiationInfo, SRP, NextProtocolNegotiation) + ++ ec_hello_extensions(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites), EcPointFormats) + ++ ec_hello_extensions(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites), EllipticCurves), Extensions1 = if Major == 3, Minor >=3 -> Extensions0 ++ hello_extensions(HashSigns); true -> Extensions0 end, ExtensionsBin = enc_hello_extensions(Extensions1), - {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, + + {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SIDLength), SessionID/binary, ?UINT16(CsLength), BinCipherSuites/binary, ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>}; @@ -1042,31 +1386,31 @@ enc_hs(#client_hello{client_version = {Major, Minor}, enc_hs(#server_hello{server_version = {Major, Minor}, random = Random, session_id = Session_ID, - cipher_suite = Cipher_suite, + cipher_suite = CipherSuite, compression_method = Comp_method, - renegotiation_info = RenegotiationInfo}, _Version) -> + renegotiation_info = RenegotiationInfo, + ec_point_formats = EcPointFormats, + elliptic_curves = EllipticCurves, + next_protocol_negotiation = NextProtocolNegotiation}, _Version) -> SID_length = byte_size(Session_ID), - Extensions = hello_extensions(RenegotiationInfo), + CipherSuites = [ssl_cipher:suite_definition(CipherSuite)], + Extensions = hello_extensions(RenegotiationInfo, NextProtocolNegotiation) + ++ ec_hello_extensions(CipherSuites, EcPointFormats) + ++ ec_hello_extensions(CipherSuites, EllipticCurves), ExtensionsBin = enc_hello_extensions(Extensions), {?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID/binary, - Cipher_suite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>}; + CipherSuite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>}; enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) -> ASN1Certs = certs_from_list(ASN1CertList), ACLen = erlang:iolist_size(ASN1Certs), {?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>}; -enc_hs(#server_key_exchange{params = #server_dh_params{ - dh_p = P, dh_g = G, dh_y = Y}, - signed_params = SignedParams, hashsign = HashSign}, Version) -> - PLen = byte_size(P), - GLen = byte_size(G), - YLen = byte_size(Y), - Signature = enc_sign(HashSign, SignedParams, Version), - {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P/binary, - ?UINT16(GLen), G/binary, - ?UINT16(YLen), Y/binary, - Signature/binary>> - }; +enc_hs(#server_key_exchange{exchange_keys = Keys}, _Version) -> + {?SERVER_KEY_EXCHANGE, Keys}; +enc_hs(#server_key_params{params_bin = Keys, hashsign = HashSign, + signature = Signature}, Version) -> + EncSign = enc_sign(HashSign, Signature, Version), + {?SERVER_KEY_EXCHANGE, <<Keys/binary, EncSign/binary>>}; enc_hs(#certificate_request{certificate_types = CertTypes, hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos}, certificate_authorities = CertAuths}, @@ -1108,8 +1452,67 @@ enc_cke(#encrypted_premaster_secret{premaster_secret = PKEPMS}, _) -> <<?UINT16(PKEPMSLen), PKEPMS/binary>>; enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) -> Len = byte_size(DHPublic), - <<?UINT16(Len), DHPublic/binary>>. + <<?UINT16(Len), DHPublic/binary>>; +enc_cke(#client_ec_diffie_hellman_public{dh_public = DHPublic}, _) -> + Len = byte_size(DHPublic), + <<?BYTE(Len), DHPublic/binary>>; +enc_cke(#client_psk_identity{identity = undefined}, _) -> + Id = <<"psk_identity">>, + Len = byte_size(Id), + <<?UINT16(Len), Id/binary>>; +enc_cke(#client_psk_identity{identity = Id}, _) -> + Len = byte_size(Id), + <<?UINT16(Len), Id/binary>>; +enc_cke(Identity = #client_dhe_psk_identity{identity = undefined}, Version) -> + enc_cke(Identity#client_dhe_psk_identity{identity = <<"psk_identity">>}, Version); +enc_cke(#client_dhe_psk_identity{identity = Id, dh_public = DHPublic}, _) -> + Len = byte_size(Id), + DHLen = byte_size(DHPublic), + <<?UINT16(Len), Id/binary, ?UINT16(DHLen), DHPublic/binary>>; +enc_cke(Identity = #client_rsa_psk_identity{identity = undefined}, Version) -> + enc_cke(Identity#client_rsa_psk_identity{identity = <<"psk_identity">>}, Version); +enc_cke(#client_rsa_psk_identity{identity = Id, exchange_keys = ExchangeKeys}, Version) -> + EncPMS = enc_cke(ExchangeKeys, Version), + Len = byte_size(Id), + <<?UINT16(Len), Id/binary, EncPMS/binary>>; +enc_cke(#client_srp_public{srp_a = A}, _) -> + Len = byte_size(A), + <<?UINT16(Len), A/binary>>. + +enc_server_key(#server_dh_params{dh_p = P, dh_g = G, dh_y = Y}) -> + PLen = byte_size(P), + GLen = byte_size(G), + YLen = byte_size(Y), + <<?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>; +enc_server_key(#server_ecdh_params{curve = {namedCurve, ECCurve}, public = ECPubKey}) -> + %%TODO: support arbitrary keys + KLen = size(ECPubKey), + <<?BYTE(?NAMED_CURVE_TYPE), ?UINT16((ssl_tls1:oid_to_enum(ECCurve))), + ?BYTE(KLen), ECPubKey/binary>>; +enc_server_key(#server_psk_params{hint = PskIdentityHint}) -> + Len = byte_size(PskIdentityHint), + <<?UINT16(Len), PskIdentityHint/binary>>; +enc_server_key(Params = #server_dhe_psk_params{hint = undefined}) -> + enc_server_key(Params#server_dhe_psk_params{hint = <<>>}); +enc_server_key(#server_dhe_psk_params{ + hint = PskIdentityHint, + dh_params = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y}}) -> + Len = byte_size(PskIdentityHint), + PLen = byte_size(P), + GLen = byte_size(G), + YLen = byte_size(Y), + <<?UINT16(Len), PskIdentityHint/binary, + ?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>; +enc_server_key(#server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B}) -> + NLen = byte_size(N), + GLen = byte_size(G), + SLen = byte_size(S), + BLen = byte_size(B), + <<?UINT16(NLen), N/binary, ?UINT16(GLen), G/binary, + ?BYTE(SLen), S/binary, ?UINT16(BLen), B/binary>>. +enc_sign({_, anon}, _Sign, _Version) -> + <<>>; enc_sign({HashAlg, SignAlg}, Signature, _Version = {Major, Minor}) when Major == 3, Minor >= 3-> SignLen = byte_size(Signature), @@ -1119,14 +1522,62 @@ enc_sign(_HashSign, Sign, _Version) -> SignLen = byte_size(Sign), <<?UINT16(SignLen), Sign/binary>>. -hello_extensions(undefined) -> - []; + +ec_hello_extensions(CipherSuites, #elliptic_curves{} = Info) -> + case advertises_ec_ciphers(CipherSuites) of + true -> + [Info]; + false -> + [] + end; +ec_hello_extensions(CipherSuites, #ec_point_formats{} = Info) -> + case advertises_ec_ciphers(CipherSuites) of + true -> + [Info]; + false -> + [] + end; +ec_hello_extensions(_, undefined) -> + []. + +hello_extensions(RenegotiationInfo, NextProtocolNegotiation) -> + hello_extensions(RenegotiationInfo) ++ next_protocol_extension(NextProtocolNegotiation). + +hello_extensions(RenegotiationInfo, SRP, NextProtocolNegotiation) -> + hello_extensions(RenegotiationInfo) + ++ hello_extensions(SRP) + ++ next_protocol_extension(NextProtocolNegotiation). + +advertises_ec_ciphers([]) -> + false; +advertises_ec_ciphers([{ecdh_ecdsa, _,_,_} | _]) -> + true; +advertises_ec_ciphers([{ecdhe_ecdsa, _,_,_} | _]) -> + true; +advertises_ec_ciphers([{ecdh_rsa, _,_,_} | _]) -> + true; +advertises_ec_ciphers([{ecdhe_rsa, _,_,_} | _]) -> + true; +advertises_ec_ciphers([{ecdh_anon, _,_,_} | _]) -> + true; +advertises_ec_ciphers([_| Rest]) -> + advertises_ec_ciphers(Rest). + %% Renegotiation info hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) -> []; hello_extensions(#renegotiation_info{} = Info) -> [Info]; +hello_extensions(#srp{} = Info) -> + [Info]; hello_extensions(#hash_sign_algos{} = Info) -> + [Info]; +hello_extensions(undefined) -> + []. + +next_protocol_extension(undefined) -> + []; +next_protocol_extension(#next_protocol_negotiation{} = Info) -> [Info]. enc_hello_extensions(Extensions) -> @@ -1137,6 +1588,9 @@ enc_hello_extensions([], Acc) -> Size = byte_size(Acc), <<?UINT16(Size), Acc/binary>>; +enc_hello_extensions([#next_protocol_negotiation{extension_data = ExtensionData} | Rest], Acc) -> + Len = byte_size(ExtensionData), + enc_hello_extensions(Rest, <<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData/binary, Acc/binary>>); enc_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = Info} | Rest], Acc) -> Len = byte_size(Info), enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info/binary, Acc/binary>>); @@ -1145,14 +1599,36 @@ enc_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest InfoLen = byte_size(Info), Len = InfoLen +1, enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>); - +enc_hello_extensions([#elliptic_curves{elliptic_curve_list = EllipticCurves} | Rest], Acc) -> + EllipticCurveList = << <<(ssl_tls1:oid_to_enum(X)):16>> || X <- EllipticCurves>>, + ListLen = byte_size(EllipticCurveList), + Len = ListLen + 2, + enc_hello_extensions(Rest, <<?UINT16(?ELLIPTIC_CURVES_EXT), + ?UINT16(Len), ?UINT16(ListLen), EllipticCurveList/binary, Acc/binary>>); +enc_hello_extensions([#ec_point_formats{ec_point_format_list = ECPointFormats} | Rest], Acc) -> + ECPointFormatList = list_to_binary(ECPointFormats), + ListLen = byte_size(ECPointFormatList), + Len = ListLen + 1, + enc_hello_extensions(Rest, <<?UINT16(?EC_POINT_FORMATS_EXT), + ?UINT16(Len), ?BYTE(ListLen), ECPointFormatList/binary, Acc/binary>>); +enc_hello_extensions([#srp{username = UserName} | Rest], Acc) -> + SRPLen = byte_size(UserName), + Len = SRPLen + 2, + enc_hello_extensions(Rest, <<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), UserName/binary, Acc/binary>>); enc_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) -> SignAlgoList = << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> || {Hash, Sign} <- HashSignAlgos >>, ListLen = byte_size(SignAlgoList), Len = ListLen + 2, - enc_hello_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), ?UINT16(ListLen), SignAlgoList/binary, Acc/binary>>). + enc_hello_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT), + ?UINT16(Len), ?UINT16(ListLen), SignAlgoList/binary, Acc/binary>>). +encode_client_protocol_negotiation(undefined, _) -> + undefined; +encode_client_protocol_negotiation(_, false) -> + #next_protocol_negotiation{extension_data = <<>>}; +encode_client_protocol_negotiation(_, _) -> + undefined. from_3bytes(Bin3) -> from_3bytes(Bin3, []). @@ -1173,9 +1649,15 @@ from_2bytes(<<?UINT16(N), Rest/binary>>, Acc) -> certificate_types({KeyExchange, _, _, _}) when KeyExchange == rsa; KeyExchange == dhe_dss; - KeyExchange == dhe_rsa -> + KeyExchange == dhe_rsa; + KeyExchange == ecdhe_rsa -> <<?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>; +certificate_types({KeyExchange, _, _, _}) + when KeyExchange == dh_ecdsa; + KeyExchange == dhe_ecdsa -> + <<?BYTE(?ECDSA_SIGN)>>; + certificate_types(_) -> <<?BYTE(?RSA_SIGN)>>. @@ -1192,9 +1674,6 @@ certificate_authorities(CertDbHandle, CertDbRef) -> Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) -> OTPSubj = TBSCert#'OTPTBSCertificate'.subject, DNEncodedBin = public_key:pkix_encode('Name', OTPSubj, otp), - %%Subj = public_key:pkix_transform(OTPSubj, encode), - %% {ok, DNEncoded} = 'OTP-PUB-KEY':encode('Name', Subj), - %% DNEncodedBin = iolist_to_binary(DNEncoded), DNEncodedLen = byte_size(DNEncodedBin), <<?UINT16(DNEncodedLen), DNEncodedBin/binary>> end, @@ -1206,16 +1685,18 @@ certificate_authorities_from_db(CertDbHandle, CertDbRef) -> (_, Acc) -> Acc end, - ssl_certificate_db:foldl(ConnectionCerts, [], CertDbHandle). + ssl_pkix_db:foldl(ConnectionCerts, [], CertDbHandle). digitally_signed({3, Minor}, Hash, HashAlgo, Key) when Minor >= 3 -> public_key:sign({digest, Hash}, HashAlgo, Key); -digitally_signed(_Version, Hash, _HashAlgo, #'DSAPrivateKey'{} = Key) -> - public_key:sign({digest, Hash}, sha, Key); +digitally_signed(_Version, Hash, HashAlgo, #'DSAPrivateKey'{} = Key) -> + public_key:sign({digest, Hash}, HashAlgo, Key); digitally_signed(_Version, Hash, _HashAlgo, #'RSAPrivateKey'{} = Key) -> public_key:encrypt_private(Hash, Key, - [{rsa_pad, rsa_pkcs1_padding}]). + [{rsa_pad, rsa_pkcs1_padding}]); +digitally_signed(_Version, Hash, HashAlgo, Key) -> + public_key:sign({digest, Hash}, HashAlgo, Key). calc_master_secret({3,0}, _PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) -> ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom); @@ -1248,6 +1729,19 @@ key_exchange_alg(rsa) -> key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss; Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon -> ?KEY_EXCHANGE_DIFFIE_HELLMAN; +key_exchange_alg(Alg) when Alg == ecdhe_rsa; Alg == ecdh_rsa; + Alg == ecdhe_ecdsa; Alg == ecdh_ecdsa; + Alg == ecdh_anon -> + ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN; +key_exchange_alg(psk) -> + ?KEY_EXCHANGE_PSK; +key_exchange_alg(dhe_psk) -> + ?KEY_EXCHANGE_DHE_PSK; +key_exchange_alg(rsa_psk) -> + ?KEY_EXCHANGE_RSA_PSK; +key_exchange_alg(Alg) + when Alg == srp_rsa; Alg == srp_dss; Alg == srp_anon -> + ?KEY_EXCHANGE_SRP; key_exchange_alg(_) -> ?NULL. @@ -1261,29 +1755,73 @@ apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) -> {unknown, {SslState, UserState}} end. -certificate_verify_rsa(Hashes, sha, Signature, PublicKey, {Major, Minor}) - when Major == 3, Minor >= 3 -> - public_key:verify({digest, Hashes}, sha, Signature, PublicKey); -certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, {Major, Minor}) - when Major == 3, Minor >= 3 -> - public_key:verify({digest, Hashes}, HashAlgo, Signature, PublicKey); -certificate_verify_rsa(Hashes, _HashAlgo, Signature, PublicKey, _Version) -> - case public_key:decrypt_public(Signature, PublicKey, - [{rsa_pad, rsa_pkcs1_padding}]) of - Hashes -> true; - _ -> false - end. - -define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}). -define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}). +-define(TLSEXT_SIGALG_ECDSA(MD), {MD, ecdsa}). --define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_RSA(MD)). +-define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_ECDSA(MD), ?TLSEXT_SIGALG_RSA(MD)). default_hash_signs() -> + HashSigns = [?TLSEXT_SIGALG(sha512), + ?TLSEXT_SIGALG(sha384), + ?TLSEXT_SIGALG(sha256), + ?TLSEXT_SIGALG(sha224), + ?TLSEXT_SIGALG(sha), + ?TLSEXT_SIGALG_DSA(sha), + ?TLSEXT_SIGALG_RSA(md5)], + CryptoSupport = proplists:get_value(public_keys, crypto:supports()), + HasECC = proplists:get_bool(ecdsa, CryptoSupport), #hash_sign_algos{hash_sign_algos = - [?TLSEXT_SIGALG(sha512), - ?TLSEXT_SIGALG(sha384), - ?TLSEXT_SIGALG(sha256), - ?TLSEXT_SIGALG(sha), - ?TLSEXT_SIGALG_DSA(sha), - ?TLSEXT_SIGALG_RSA(md5)]}. + lists:filter(fun({_, ecdsa}) -> HasECC; + (_) -> true end, HashSigns)}. + +handle_hello_extensions(#client_hello{random = Random, + cipher_suites = CipherSuites, + renegotiation_info = Info, + srp = SRP, + ec_point_formats = EcPointFormats0, + elliptic_curves = EllipticCurves0} = Hello, Version, + #ssl_options{secure_renegotiate = SecureRenegotation} = Opts, + Session0, ConnectionStates0, Renegotiation) -> + Session = handle_srp_extension(SRP, Session0), + ConnectionStates = handle_renegotiation_extension(Version, Info, Random, Session, ConnectionStates0, + Renegotiation, SecureRenegotation, CipherSuites), + ProtocolsToAdvertise = handle_next_protocol_extension(Hello, Renegotiation, Opts), + {EcPointFormats, EllipticCurves} = handle_ecc_extensions(Version, EcPointFormats0, EllipticCurves0), + %%TODO make extensions compund data structure + {Session, ConnectionStates, ProtocolsToAdvertise, EcPointFormats, EllipticCurves}. + + +handle_renegotiation_extension(Version, Info, Random, #session{cipher_suite = CipherSuite, + compression_method = Compression}, + ConnectionStates0, Renegotiation, SecureRenegotation, CipherSuites) -> + case handle_renegotiation_info(server, Info, ConnectionStates0, + Renegotiation, SecureRenegotation, + CipherSuites) of + {ok, ConnectionStates1} -> + hello_pending_connection_states(server, + Version, + CipherSuite, + Random, + Compression, + ConnectionStates1); + #alert{} = Alert -> + throw(Alert) + end. + +handle_next_protocol_extension(Hello, Renegotiation, SslOpts)-> + case handle_next_protocol_on_server(Hello, Renegotiation, SslOpts) of + #alert{} = Alert -> + throw(Alert); + ProtocolsToAdvertise -> + ProtocolsToAdvertise + end. + +handle_srp_extension(undefined, Session) -> + Session; +handle_srp_extension(#srp{username = Username}, Session) -> + Session#session{srp_username = Username}. + +int_to_bin(I) -> + L = (length(integer_to_list(I, 16)) + 1) div 2, + <<I:(L*8)>>. diff --git a/lib/ssl/src/tls_handshake.hrl b/lib/ssl/src/tls_handshake.hrl new file mode 100644 index 0000000000..abf1b5abb6 --- /dev/null +++ b/lib/ssl/src/tls_handshake.hrl @@ -0,0 +1,45 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013-2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Record and constant defenitions for the TLS-handshake protocol +%% see RFC 5246. +%%---------------------------------------------------------------------- +-ifndef(tls_handshake). +-define(tls_handshake, true). + +-include("ssl_handshake.hrl"). %% Common TLS and DTLS records and Constantes + +-record(client_hello, { + client_version, + random, + session_id, % opaque SessionID<0..32> + cipher_suites, % cipher_suites<2..2^16-1> + compression_methods, % compression_methods<1..2^8-1>, + %% Extensions + renegotiation_info, + hash_signs, % supported combinations of hashes/signature algos + next_protocol_negotiation = undefined, % [binary()] + srp, + ec_point_formats, + elliptic_curves + }). + +-endif. % -ifdef(tls_handshake). diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/tls_record.erl index 8e93ce4634..1409a04763 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -23,12 +23,12 @@ %% %%---------------------------------------------------------------------- --module(ssl_record). +-module(tls_record). --include("ssl_record.hrl"). +-include("tls_record.hrl"). -include("ssl_internal.hrl"). -include("ssl_alert.hrl"). --include("ssl_handshake.hrl"). +-include("tls_handshake.hrl"). -include("ssl_cipher.hrl"). %% Connection state handling @@ -56,7 +56,7 @@ %% Misc. -export([protocol_version/1, lowest_protocol_version/2, highest_protocol_version/1, supported_protocol_versions/0, - is_acceptable_version/1]). + is_acceptable_version/1, is_acceptable_version/2]). -export([compressions/0]). @@ -463,10 +463,9 @@ supported_protocol_versions() -> supported_protocol_versions([]) -> Vsns = case sufficient_tlsv1_2_crypto_support() of true -> - %%?ALL_SUPPORTED_VERSIONS; %% Add TlS-1.2 as default in R16 - ?DEFAULT_SUPPORTED_VERSIONS; + ?ALL_SUPPORTED_VERSIONS; false -> - ?DEFAULT_SUPPORTED_VERSIONS + ?MIN_SUPPORTED_VERSIONS end, application:set_env(ssl, protocol_version, Vsns), Vsns; @@ -476,8 +475,10 @@ supported_protocol_versions([_|_] = Vsns) -> %%-------------------------------------------------------------------- -spec is_acceptable_version(tls_version()) -> boolean(). +-spec is_acceptable_version(tls_version(), Supported :: [tls_version()]) -> boolean(). %% %% Description: ssl version 2 is not acceptable security risks are too big. +%% %%-------------------------------------------------------------------- is_acceptable_version({N,_}) when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION -> @@ -485,6 +486,12 @@ is_acceptable_version({N,_}) is_acceptable_version(_) -> false. +is_acceptable_version({N,_} = Version, Versions) + when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION -> + lists:member(Version, Versions); +is_acceptable_version(_,_) -> + false. + %%-------------------------------------------------------------------- -spec compressions() -> [binary()]. %% @@ -705,12 +712,5 @@ mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) Length, Fragment). sufficient_tlsv1_2_crypto_support() -> - Data = "Sampl", - Data2 = "e #1", - Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39, - 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>, - try - crypto:sha256_mac(Key, lists:flatten([Data, Data2])), - true - catch _:_ -> false - end. + CryptoSupport = crypto:supports(), + proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)). diff --git a/lib/ssl/src/tls_record.hrl b/lib/ssl/src/tls_record.hrl new file mode 100644 index 0000000000..c9350fa137 --- /dev/null +++ b/lib/ssl/src/tls_record.hrl @@ -0,0 +1,39 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013-2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Record and constant defenitions for the TLS-record protocol +%% see RFC 5246 +%%---------------------------------------------------------------------- + +-ifndef(tls_record). +-define(tls_record, true). + +-include("ssl_record.hrl"). %% Common TLS and DTLS records and Constantes + +%% Used to handle tls_plain_text, tls_compressed and tls_cipher_text + +-record(ssl_tls, { + type, + version, + fragment + }). + +-endif. % -ifdef(tls_record). diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile index 343157b22e..39aa22ffb4 100644 --- a/lib/ssl/test/Makefile +++ b/lib/ssl/test/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2012. All Rights Reserved. +# Copyright Ericsson AB 1999-2013. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -37,13 +37,16 @@ VSN=$(GS_VSN) MODULES = \ ssl_test_lib \ ssl_basic_SUITE \ + ssl_cipher_SUITE \ + ssl_certificate_verify_SUITE\ + ssl_dist_SUITE \ ssl_handshake_SUITE \ + ssl_npn_hello_SUITE \ + ssl_npn_handshake_SUITE \ ssl_packet_SUITE \ - ssl_cipher_SUITE \ ssl_payload_SUITE \ - ssl_to_openssl_SUITE \ ssl_session_cache_SUITE \ - ssl_dist_SUITE \ + ssl_to_openssl_SUITE \ make_certs\ erl_make_certs @@ -55,8 +58,10 @@ HRL_FILES = HRL_FILES_SRC = \ ssl_internal.hrl\ ssl_alert.hrl \ + tls_handshake.hrl \ ssl_handshake.hrl \ ssl_cipher.hrl \ + tls_record.hrl \ ssl_record.hrl HRL_FILES_INC = diff --git a/lib/ssl/test/erl_make_certs.erl b/lib/ssl/test/erl_make_certs.erl index 254aa6d2f9..22dc951ac1 100644 --- a/lib/ssl/test/erl_make_certs.erl +++ b/lib/ssl/test/erl_make_certs.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -45,7 +45,7 @@ %% {dnQualifer, DnQ} %% issuer = {Issuer, IssuerKey} true (i.e. a ca cert is created) %% (obs IssuerKey migth be {Key, Password} -%% key = KeyFile|KeyBin|rsa|dsa Subject PublicKey rsa or dsa generates key +%% key = KeyFile|KeyBin|rsa|dsa|ec Subject PublicKey rsa, dsa or ec generates key %% %% %% (OBS: The generated keys are for testing only) @@ -91,6 +91,16 @@ gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) -> {Key, encode_key(Key)}. %%-------------------------------------------------------------------- +%% @doc Creates a ec key (OBS: for testing only) +%% the sizes are in bytes +%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()} +%% @end +%%-------------------------------------------------------------------- +gen_ec(Curve) when is_atom(Curve) -> + Key = gen_ec2(Curve), + {Key, encode_key(Key)}. + +%%-------------------------------------------------------------------- %% @doc Verifies cert signatures %% @spec (::binary(), ::tuple()) -> ::boolean() %% @end @@ -102,7 +112,10 @@ verify_signature(DerEncodedCert, DerKey, _KeyParams) -> public_key:pkix_verify(DerEncodedCert, #'RSAPublicKey'{modulus=Mod, publicExponent=Exp}); #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} -> - public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}) + public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}); + #'ECPrivateKey'{version = _Version, privateKey = _PrivKey, + parameters = Params, publicKey = {0, PubKey}} -> + public_key:pkix_verify(DerEncodedCert, {#'ECPoint'{point = PubKey}, Params}) end. %%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -112,6 +125,7 @@ get_key(Opts) -> undefined -> make_key(rsa, Opts); rsa -> make_key(rsa, Opts); dsa -> make_key(dsa, Opts); + ec -> make_key(ec, Opts); Key -> Password = proplists:get_value(password, Opts, no_passwd), decode_key(Key, Password) @@ -129,6 +143,8 @@ decode_key(#'RSAPrivateKey'{} = Key,_) -> Key; decode_key(#'DSAPrivateKey'{} = Key,_) -> Key; +decode_key(#'ECPrivateKey'{} = Key,_) -> + Key; decode_key(PemEntry = {_,_,_}, Pw) -> public_key:pem_entry_decode(PemEntry, Pw); decode_key(PemBin, Pw) -> @@ -137,10 +153,13 @@ decode_key(PemBin, Pw) -> encode_key(Key = #'RSAPrivateKey'{}) -> {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key), - {'RSAPrivateKey', list_to_binary(Der), not_encrypted}; + {'RSAPrivateKey', Der, not_encrypted}; encode_key(Key = #'DSAPrivateKey'{}) -> {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key), - {'DSAPrivateKey', list_to_binary(Der), not_encrypted}. + {'DSAPrivateKey', Der, not_encrypted}; +encode_key(Key = #'ECPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('ECPrivateKey', Key), + {'ECPrivateKey', Der, not_encrypted}. make_tbs(SubjectKey, Opts) -> Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))), @@ -277,7 +296,14 @@ publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) -> publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) -> Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa', parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}}, - #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}. + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}; +publickey(#'ECPrivateKey'{version = _Version, + privateKey = _PrivKey, + parameters = Params, + publicKey = {0, PubKey}}) -> + Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-ecPublicKey', parameters=Params}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, + subjectPublicKey = #'ECPoint'{point = PubKey}}. validity(Opts) -> DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1), @@ -298,13 +324,24 @@ sign_algorithm(#'RSAPrivateKey'{}, Opts) -> end, {Type, 'NULL'}; sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) -> - {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}. + {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}; +sign_algorithm(#'ECPrivateKey'{}, Opts) -> + Type = case proplists:get_value(digest, Opts, sha1) of + sha1 -> ?'ecdsa-with-SHA1'; + sha512 -> ?'ecdsa-with-SHA512'; + sha384 -> ?'ecdsa-with-SHA384'; + sha256 -> ?'ecdsa-with-SHA256' + end, + {Type, 'NULL'}. make_key(rsa, _Opts) -> %% (OBS: for testing only) gen_rsa2(64); make_key(dsa, _Opts) -> - gen_dsa2(128, 20). %% Bytes i.e. {1024, 160} + gen_dsa2(128, 20); %% Bytes i.e. {1024, 160} +make_key(ec, _Opts) -> + %% (OBS: for testing only) + gen_ec2(secp256k1). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% RSA key generation (OBS: for testing only) @@ -349,24 +386,37 @@ gen_dsa2(LSize, NSize) -> X0 = prime(LSize), P0 = prime((LSize div 2) +1), - %% Choose L-bit prime modulus P such that p–1 is a multiple of q. + %% Choose L-bit prime modulus P such that p-1 is a multiple of q. case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of error -> gen_dsa2(LSize, NSize); P -> - G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q. - %% such that This may be done by setting g = h^(p–1)/q mod p, commonly h=2 is used. + G = crypto:mod_pow(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q. + %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used. X = prime(20), %% Choose x by some random method, where 0 < x < q. - Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p. + Y = crypto:mod_pow(G, X, P), %% Calculate y = g^x mod p. - #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X} + #'DSAPrivateKey'{version=0, p = P, q = Q, + g = crypto:bytes_to_integer(G), y = crypto:bytes_to_integer(Y), x = X} end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EC key generation (OBS: for testing only) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +gen_ec2(CurveId) -> + {PubKey, PrivKey} = crypto:generate_key(ecdh, CurveId), + + #'ECPrivateKey'{version = 1, + privateKey = binary_to_list(PrivKey), + parameters = {namedCurve, pubkey_cert_records:namedCurves(CurveId)}, + publicKey = {0, PubKey}}. + %% See fips_186-3.pdf dsa_search(T, P0, Q, Iter) when Iter > 0 -> P = 2*T*Q*P0 + 1, - case is_prime(crypto:mpint(P), 50) of + case is_prime(P, 50) of true -> P; false -> dsa_search(T+1, P0, Q, Iter-1) end; @@ -377,38 +427,40 @@ dsa_search(_,_,_,_) -> %%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% prime(ByteSize) -> Rand = odd_rand(ByteSize), - crypto:erlint(prime_odd(Rand, 0)). + prime_odd(Rand, 0). prime_odd(Rand, N) -> case is_prime(Rand, 50) of true -> Rand; false -> - NotPrime = crypto:erlint(Rand), - prime_odd(crypto:mpint(NotPrime+2), N+1) + prime_odd(Rand+2, N+1) end. %% see http://en.wikipedia.org/wiki/Fermat_primality_test is_prime(_, 0) -> true; is_prime(Candidate, Test) -> - CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate), - case crypto:mod_exp(CoPrime, Candidate, Candidate) of - CoPrime -> is_prime(Candidate, Test-1); - _ -> false - end. + CoPrime = odd_rand(10000, Candidate), + Result = crypto:mod_pow(CoPrime, Candidate, Candidate) , + is_prime(CoPrime, crypto:bytes_to_integer(Result), Candidate, Test). + +is_prime(CoPrime, CoPrime, Candidate, Test) -> + is_prime(Candidate, Test-1); +is_prime(_,_,_,_) -> + false. odd_rand(Size) -> Min = 1 bsl (Size*8-1), Max = (1 bsl (Size*8))-1, - odd_rand(crypto:mpint(Min), crypto:mpint(Max)). + odd_rand(Min, Max). odd_rand(Min,Max) -> - Rand = <<Sz:32, _/binary>> = crypto:rand_uniform(Min,Max), - BitSkip = (Sz+4)*8-1, - case Rand of - Odd = <<_:BitSkip, 1:1>> -> Odd; - Even = <<_:BitSkip, 0:1>> -> - crypto:mpint(crypto:erlint(Even)+1) + Rand = crypto:rand_uniform(Min,Max), + case Rand rem 2 of + 0 -> + Rand + 1; + _ -> + Rand end. extended_gcd(A, B) -> @@ -427,3 +479,4 @@ pem_to_der(File) -> der_to_pem(File, Entries) -> PemBin = public_key:pem_encode(Entries), file:write_file(File, PemBin). + diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl index 693289990c..4603a9f846 100644 --- a/lib/ssl/test/make_certs.erl +++ b/lib/ssl/test/make_certs.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -121,7 +121,19 @@ create_self_signed_cert(Root, OpenSSLCmd, CAName, Cnf) -> " -keyout ", KeyFile, " -out ", CertFile], Env = [{"ROOTDIR", Root}], - cmd(Cmd, Env). + cmd(Cmd, Env), + fix_key_file(OpenSSLCmd, KeyFile). + +% openssl 1.0 generates key files in pkcs8 format by default and we don't handle this format +fix_key_file(OpenSSLCmd, KeyFile) -> + KeyFileTmp = KeyFile ++ ".tmp", + Cmd = [OpenSSLCmd, " rsa", + " -in ", + KeyFile, + " -out ", + KeyFileTmp], + cmd(Cmd, []), + ok = file:rename(KeyFileTmp, KeyFile). create_ca_dir(Root, CAName, Cnf) -> CARoot = filename:join([Root, CAName]), @@ -139,7 +151,8 @@ create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile) -> " -keyout ", KeyFile, " -out ", ReqFile], Env = [{"ROOTDIR", Root}], - cmd(Cmd, Env). + cmd(Cmd, Env), + fix_key_file(OpenSSLCmd, KeyFile). sign_req(Root, OpenSSLCmd, CA, CertType, ReqFile, CertFile) -> CACnfFile = filename:join([Root, CA, "ca.cnf"]), diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 112ed85ec5..b5cf6d1212 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -30,8 +30,8 @@ -include("ssl_internal.hrl"). -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). --include("ssl_record.hrl"). --include("ssl_handshake.hrl"). +-include("tls_record.hrl"). +-include("tls_handshake.hrl"). -define('24H_in_sec', 86400). -define(TIMEOUT, 60000). @@ -41,127 +41,10 @@ -define(RENEGOTIATION_DISABLE_TIME, 12000). -define(CLEAN_SESSION_DB, 60000). -%% Test server callback functions %%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initialization before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config0) -> - Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), - catch crypto:stop(), - try crypto:start() of - ok -> - application:start(public_key), - - %% make rsa certs using oppenssl - Result = - (catch make_certs:all(?config(data_dir, Config0), - ?config(priv_dir, Config0))), - test_server:format("Make certs ~p~n", [Result]), - - Config1 = ssl_test_lib:make_dsa_cert(Config0), - Config = ssl_test_lib:cert_options(Config1), - [{watchdog, Dog} | Config] - catch _:_ -> - {skip, "Crypto did not start"} - end. -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- -end_per_suite(_Config) -> - ssl:stop(), - application:stop(crypto). -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initialization before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initialization before each test case -%%-------------------------------------------------------------------- -init_per_testcase(no_authority_key_identifier, Config) -> - %% Clear cach so that root cert will not - %% be found. - ssl:clear_pem_cache(), - Config; - -init_per_testcase(protocol_versions, Config) -> - ssl:stop(), - application:load(ssl), - %% For backwards compatibility sslv2 should be filtered out. - application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]), - ssl:start(), - Config; - -init_per_testcase(reuse_session_expired, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - ssl:stop(), - application:load(ssl), - application:set_env(ssl, session_lifetime, ?EXPIRE), - application:set_env(ssl, session_delay_cleanup_time, 500), - ssl:start(), - Config; - -init_per_testcase(empty_protocol_versions, Config) -> - ssl:stop(), - application:load(ssl), - application:set_env(ssl, protocol_version, []), - ssl:start(), - Config; - -%% init_per_testcase(different_ca_peer_sign, Config0) -> -%% ssl_test_lib:make_mix_cert(Config0); - -init_per_testcase(_TestCase, Config0) -> - test_server:format("TLS/SSL version ~p~n ", [ssl_record:supported_protocol_versions()]), - Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), - [{watchdog, Dog} | Config]. - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(reuse_session_expired, Config) -> - application:unset_env(ssl, session_lifetime), - application:unset_env(ssl, session_delay_cleanup_time), - end_per_testcase(default_action, Config); - -end_per_testcase(_TestCase, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end. - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> @@ -183,45 +66,28 @@ groups() -> {'tlsv1', [], all_versions_groups() ++ rizzo_tests()}, {'sslv3', [], all_versions_groups() ++ rizzo_tests()}, {api,[], api_tests()}, - {certificate_verify, [], certificate_verify_tests()}, {session, [], session_tests()}, {renegotiate, [], renegotiate_tests()}, {ciphers, [], cipher_tests()}, + {ciphers_ec, [], cipher_tests_ec()}, {error_handling_tests, [], error_handling_tests()} ]. all_versions_groups ()-> [{group, api}, - {group, certificate_verify}, {group, renegotiate}, {group, ciphers}, + {group, ciphers_ec}, {group, error_handling_tests}]. -init_per_group(GroupName, Config) -> - case ssl_test_lib:is_tls_version(GroupName) of - true -> - case ssl_test_lib:sufficient_crypto_support(GroupName) of - true -> - ssl_test_lib:init_tls_version(GroupName), - Config; - false -> - {skip, "Missing crypto support"} - end; - _ -> - ssl:start(), - Config - end. - - -end_per_group(_GroupName, Config) -> - Config. basic_tests() -> [app, alerts, send_close, connect_twice, - connect_dist + connect_dist, + clear_pem_cache ]. options_tests() -> @@ -235,19 +101,21 @@ options_tests() -> invalid_inet_set_option_not_list, invalid_inet_set_option_improper_list, dh_params, - ecertfile, - ecacertfile, - ekeyfile, - eoptions, + invalid_certfile, + invalid_cacertfile, + invalid_keyfile, + invalid_options, protocol_versions, empty_protocol_versions, ipv6, - reuseaddr]. + reuseaddr, + tcp_reuseaddr]. api_tests() -> [connection_info, peername, peercert, + peercert_with_client_cert, sockname, versions, controlling_process, @@ -258,39 +126,10 @@ api_tests() -> shutdown_both, shutdown_error, hibernate, + listen_socket, ssl_accept_timeout, - ssl_recv_timeout - ]. - -certificate_verify_tests() -> - [server_verify_peer_passive, - server_verify_peer_active, - server_verify_peer_active_once, - server_verify_none_passive, - server_verify_none_active, - server_verify_none_active_once, - server_verify_no_cacerts, - server_require_peer_cert_ok, - server_require_peer_cert_fail, - server_verify_client_once_passive, - server_verify_client_once_active, - server_verify_client_once_active_once, - client_verify_none_passive, - client_verify_none_active, - client_verify_none_active_once, - extended_key_usage_verify_peer, - extended_key_usage_verify_none, - invalid_signature_client, - invalid_signature_server, - cert_expired, - client_with_cert_cipher_suites_handshake, - verify_fun_always_run_client, - verify_fun_always_run_server, - unknown_server_ca_fail, - unknown_server_ca_accept_verify_none, - unknown_server_ca_accept_verify_peer, - unknown_server_ca_accept_backwardscompatibility, - no_authority_key_identifier + ssl_recv_timeout, + versions_option ]. session_tests() -> @@ -317,8 +156,21 @@ cipher_tests() -> ciphers_dsa_signed_certs, ciphers_dsa_signed_certs_openssl_names, anonymous_cipher_suites, + psk_cipher_suites, + psk_with_hint_cipher_suites, + psk_anon_cipher_suites, + psk_anon_with_hint_cipher_suites, + srp_cipher_suites, + srp_anon_cipher_suites, + srp_dsa_cipher_suites, default_reject_anonymous]. +cipher_tests_ec() -> + [ciphers_ecdsa_signed_certs, + ciphers_ecdsa_signed_certs_openssl_names, + ciphers_ecdh_rsa_signed_certs, + ciphers_ecdh_rsa_signed_certs_openssl_names]. + error_handling_tests()-> [controller_dies, client_closes_socket, @@ -332,19 +184,110 @@ rizzo_tests() -> [rizzo, no_rizzo_rc4]. -%% Test cases starts here. %%-------------------------------------------------------------------- -app(doc) -> - "Test that the ssl app file is ok"; -app(suite) -> - []; +init_per_suite(Config0) -> + Dog = ct:timetrap(?LONG_TIMEOUT *2), + catch crypto:stop(), + try crypto:start() of + ok -> + ssl:start(), + %% make rsa certs using oppenssl + Result = + (catch make_certs:all(?config(data_dir, Config0), + ?config(priv_dir, Config0))), + ct:log("Make certs ~p~n", [Result]), + + Config1 = ssl_test_lib:make_dsa_cert(Config0), + Config2 = ssl_test_lib:make_ecdsa_cert(Config1), + Config3 = ssl_test_lib:make_ecdh_rsa_cert(Config2), + Config = ssl_test_lib:cert_options(Config3), + [{watchdog, Dog} | Config] + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). + +%%-------------------------------------------------------------------- +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName), + Config; + false -> + {skip, "Missing crypto support"} + end; + _ -> + ssl:start(), + Config + end. + + +end_per_group(_GroupName, Config) -> + Config. + +%%-------------------------------------------------------------------- +init_per_testcase(no_authority_key_identifier, Config) -> + %% Clear cach so that root cert will not + %% be found. + ssl:clear_pem_cache(), + Config; + +init_per_testcase(protocol_versions, Config) -> + ssl:stop(), + application:load(ssl), + %% For backwards compatibility sslv2 should be filtered out. + application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]), + ssl:start(), + Config; + +init_per_testcase(reuse_session_expired, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + ssl:stop(), + application:load(ssl), + application:set_env(ssl, session_lifetime, ?EXPIRE), + application:set_env(ssl, session_delay_cleanup_time, 500), + ssl:start(), + Config; + +init_per_testcase(empty_protocol_versions, Config) -> + ssl:stop(), + application:load(ssl), + application:set_env(ssl, protocol_version, []), + ssl:start(), + Config; + +%% init_per_testcase(different_ca_peer_sign, Config0) -> +%% ssl_test_lib:make_mix_cert(Config0); + +init_per_testcase(_TestCase, Config0) -> + ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]), + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ct:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. + +end_per_testcase(reuse_session_expired, Config) -> + application:unset_env(ssl, session_lifetime), + application:unset_env(ssl, session_delay_cleanup_time), + end_per_testcase(default_action, Config); + +end_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +app() -> + [{doc, "Test that the ssl app file is ok"}]. app(Config) when is_list(Config) -> - ok = test_server:app_test(ssl). + ok = ?t:app_test(ssl). %%-------------------------------------------------------------------- -alerts(doc) -> - "Test ssl_alert:alert_txt/1"; -alerts(suite) -> - []; +alerts() -> + [{doc, "Test ssl_alert:alert_txt/1"}]. alerts(Config) when is_list(Config) -> Descriptions = [?CLOSE_NOTIFY, ?UNEXPECTED_MESSAGE, ?BAD_RECORD_MAC, ?DECRYPTION_FAILED, ?RECORD_OVERFLOW, ?DECOMPRESSION_FAILURE, @@ -361,14 +304,12 @@ alerts(Config) when is_list(Config) -> Txt when is_list(Txt) -> ok; Other -> - test_server:fail({unexpected, Other}) + ct:fail({unexpected, Other}) end end, Alerts). %%-------------------------------------------------------------------- -connection_info(doc) -> - ["Test the API function ssl:connection_info/1"]; -connection_info(suite) -> - []; +connection_info() -> + [{doc,"Test the API function ssl:connection_info/1"}]. connection_info(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -387,11 +328,11 @@ connection_info(Config) when is_list(Config) -> [{ciphers,[{rsa,rc4_128,sha,no_export}]} | ClientOpts]}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), Version = - ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + tls_record:protocol_version(tls_record:highest_protocol_version([])), ServerMsg = ClientMsg = {ok, {Version, {rsa,rc4_128,sha}}}, @@ -400,58 +341,23 @@ connection_info(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). -connection_info_result(Socket) -> - ssl:connection_info(Socket). - %%-------------------------------------------------------------------- - -protocol_versions(doc) -> - ["Test to set a list of protocol versions in app environment."]; - -protocol_versions(suite) -> - []; +protocol_versions() -> + [{doc,"Test to set a list of protocol versions in app environment."}]. protocol_versions(Config) when is_list(Config) -> basic_test(Config). - -empty_protocol_versions(doc) -> - ["Test to set an empty list of protocol versions in app environment."]; - -empty_protocol_versions(suite) -> - []; +%%-------------------------------------------------------------------- +empty_protocol_versions() -> + [{doc,"Test to set an empty list of protocol versions in app environment."}]. empty_protocol_versions(Config) when is_list(Config) -> basic_test(Config). - -basic_test(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, ClientOpts}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -controlling_process(doc) -> - ["Test API function controlling_process/2"]; - -controlling_process(suite) -> - []; +controlling_process() -> + [{doc,"Test API function controlling_process/2"}]. controlling_process(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -475,7 +381,7 @@ controlling_process(Config) when is_list(Config) -> ClientMsg]}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), receive @@ -494,46 +400,15 @@ controlling_process(Config) when is_list(Config) -> ok end; Unexpected -> - test_server:fail(Unexpected) + ct:fail(Unexpected) end, ssl_test_lib:close(Server), ssl_test_lib:close(Client). -controlling_process_result(Socket, Pid, Msg) -> - ok = ssl:controlling_process(Socket, Pid), - %% Make sure other side has evaluated controlling_process - %% before message is sent - test_server:sleep(?SLEEP), - ssl:send(Socket, Msg), - no_result_msg. - -receive_s_rizzo_duong_beast() -> - receive - {ssl, _, "erver hello"} -> - receive - {ssl, _, "C"} -> - receive - {ssl, _, "lient hello"} -> - ok - end - end - end. -receive_c_rizzo_duong_beast() -> - receive - {ssl, _, "lient hello"} -> - receive - {ssl, _, "S"} -> - receive - {ssl, _, "erver hello"} -> - ok - end - end - end. %%-------------------------------------------------------------------- -controller_dies(doc) -> - ["Test that the socket is closed after controlling process dies"]; -controller_dies(suite) -> []; +controller_dies() -> + [{doc,"Test that the socket is closed after controlling process dies"}]. controller_dies(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -556,8 +431,8 @@ controller_dies(Config) when is_list(Config) -> ClientMsg]}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), - test_server:sleep(?SLEEP), %% so that they are connected + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), + ct:sleep(?SLEEP), %% so that they are connected process_flag(trap_exit, true), @@ -574,7 +449,7 @@ controller_dies(Config) when is_list(Config) -> %% Make sure server finishes and verification %% and is in coonection state before %% killing client - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), Pid ! {self(), connected, Socket}, receive die_nice -> normal end end, @@ -594,13 +469,13 @@ controller_dies(Config) when is_list(Config) -> Client3 ! die_nice end, - test_server:format("Wating on exit ~p~n",[Client3]), + ct:log("Wating on exit ~p~n",[Client3]), receive {'EXIT', Client3, normal} -> ok end, receive %% Client3 is dead but that doesn't matter, socket should not be closed. Unexpected -> - test_server:format("Unexpected ~p~n",[Unexpected]), - test_server:fail({line, ?LINE-1}) + ct:log("Unexpected ~p~n",[Unexpected]), + ct:fail({line, ?LINE-1}) after 1000 -> ok end, @@ -616,39 +491,17 @@ controller_dies(Config) when is_list(Config) -> controller_dies_result, [self(), ClientMsg]}}, {options, [{reuseaddr,true}|ClientOpts]}]), - test_server:sleep(?SLEEP), %% so that they are connected + ct:sleep(?SLEEP), %% so that they are connected exit(Server, killed), get_close(Server, ?LINE), process_flag(trap_exit, false), ssl_test_lib:close(LastClient). -controller_dies_result(_Socket, _Pid, _Msg) -> - receive Result -> Result end. - -get_close(Pid, Where) -> - receive - {'EXIT', Pid, _Reason} -> - receive - {_, {ssl_closed, Socket}} -> - test_server:format("Socket closed ~p~n",[Socket]); - Unexpected -> - test_server:format("Unexpected ~p~n",[Unexpected]), - test_server:fail({line, ?LINE-1}) - after 5000 -> - test_server:fail({timeout, {line, ?LINE, Where}}) - end; - Unexpected -> - test_server:format("Unexpected ~p~n",[Unexpected]), - test_server:fail({line, ?LINE-1}) - after 5000 -> - test_server:fail({timeout, {line, ?LINE, Where}}) - end. - %%-------------------------------------------------------------------- -client_closes_socket(doc) -> - ["Test what happens when client closes socket before handshake is compleated"]; -client_closes_socket(suite) -> []; +client_closes_socket() -> + [{doc,"Test what happens when client closes socket before handshake is compleated"}]. + client_closes_socket(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -665,7 +518,7 @@ client_closes_socket(Config) when is_list(Config) -> [Hostname, Port, TcpOpts]), %% Make sure that ssl_accept is called before %% client process ends and closes socket. - test_server:sleep(?SLEEP) + ct:sleep(?SLEEP) end, _Client = spawn_link(Connect), @@ -673,11 +526,8 @@ client_closes_socket(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, {error,closed}). %%-------------------------------------------------------------------- -connect_dist(doc) -> - ["Test a simple connect as is used by distribution"]; - -connect_dist(suite) -> - []; +connect_dist() -> + [{doc,"Test a simple connect as is used by distribution"}]. connect_dist(Config) when is_list(Config) -> ClientOpts0 = ?config(client_kc_opts, Config), @@ -703,22 +553,36 @@ connect_dist(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). -connect_dist_s(S) -> - Msg = term_to_binary({erlang,term}), - ok = ssl:send(S, Msg). - -connect_dist_c(S) -> - Test = binary_to_list(term_to_binary({erlang,term})), - {ok, Test} = ssl:recv(S, 0, 10000), - ok. +%%-------------------------------------------------------------------- +clear_pem_cache() -> + [{doc,"Test that internal reference tabel is cleaned properly even when " + " the PEM cache is cleared" }]. +clear_pem_cache(Config) when is_list(Config) -> + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + [_,FilRefDb, _] = element(5, State), + {Server, Client} = basic_verify_test_no_close(Config), + 2 = ets:info(FilRefDb, size), + ssl:clear_pem_cache(), + _ = sys:get_status(whereis(ssl_manager)), + {Server1, Client1} = basic_verify_test_no_close(Config), + 4 = ets:info(FilRefDb, size), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + ct:sleep(5000), + _ = sys:get_status(whereis(ssl_manager)), + 2 = ets:info(FilRefDb, size), + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client1), + ct:sleep(5000), + _ = sys:get_status(whereis(ssl_manager)), + 0 = ets:info(FilRefDb, size). %%-------------------------------------------------------------------- -peername(doc) -> - ["Test API function peername/1"]; - -peername(suite) -> - []; +peername() -> + [{doc,"Test API function peername/1"}]. peername(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -741,7 +605,7 @@ peername(Config) when is_list(Config) -> ServerMsg = {ok, {ClientIp, ClientPort}}, ClientMsg = {ok, {ServerIp, Port}}, - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), @@ -749,14 +613,9 @@ peername(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). -peername_result(S) -> - ssl:peername(S). - %%-------------------------------------------------------------------- -peercert(doc) -> - [""]; -peercert(suite) -> - []; +peercert() -> + [{doc,"Test API function peercert/1"}]. peercert(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -779,7 +638,7 @@ peercert(Config) when is_list(Config) -> ServerMsg = {error, no_peercert}, ClientMsg = {ok, BinCert}, - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), @@ -789,14 +648,45 @@ peercert(Config) when is_list(Config) -> peercert_result(Socket) -> ssl:peercert(Socket). - %%-------------------------------------------------------------------- -sockname(doc) -> - ["Test API function sockname/1"]; -sockname(suite) -> - []; +peercert_with_client_cert() -> + [{doc,"Test API function peercert/1"}]. +peercert_with_client_cert(Config) when is_list(Config) -> + ClientOpts = ?config(client_dsa_opts, Config), + ServerOpts = ?config(server_dsa_verify_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, peercert_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, peercert_result, []}}, + {options, ClientOpts}]), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + [{'Certificate', ServerBinCert, _}]= ssl_test_lib:pem_to_der(ServerCertFile), + ClientCertFile = proplists:get_value(certfile, ClientOpts), + [{'Certificate', ClientBinCert, _}]= ssl_test_lib:pem_to_der(ClientCertFile), + + ServerMsg = {ok, ClientBinCert}, + ClientMsg = {ok, ServerBinCert}, + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +sockname() -> + [{doc,"Test API function sockname/1"}]. sockname(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -818,7 +708,7 @@ sockname(Config) when is_list(Config) -> ServerMsg = {ok, {ServerIp, Port}}, ClientMsg = {ok, {ClientIp, ClientPort}}, - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), @@ -830,11 +720,8 @@ sockname_result(S) -> ssl:sockname(S). %%-------------------------------------------------------------------- -cipher_suites(doc) -> - ["Test API function cipher_suites/0"]; - -cipher_suites(suite) -> - []; +cipher_suites() -> + [{doc,"Test API function cipher_suites/0"}]. cipher_suites(Config) when is_list(Config) -> MandatoryCipherSuite = {rsa,'3des_ede_cbc',sha}, @@ -844,11 +731,8 @@ cipher_suites(Config) when is_list(Config) -> [_|_] =ssl:cipher_suites(openssl). %%-------------------------------------------------------------------- -socket_options(doc) -> - ["Test API function getopts/2 and setopts/2"]; - -socket_options(suite) -> - []; +socket_options() -> + [{doc,"Test API function getopts/2 and setopts/2"}]. socket_options(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -897,16 +781,13 @@ socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) -> ssl:setopts(Socket, [{nodelay, true}]), {ok,[{nodelay, true}]} = ssl:getopts(Socket, [nodelay]), {ok, All} = ssl:getopts(Socket, []), - test_server:format("All opts ~p~n", [All]), + ct:log("All opts ~p~n", [All]), ok. %%-------------------------------------------------------------------- -invalid_inet_get_option(doc) -> - ["Test handling of invalid inet options in getopts"]; - -invalid_inet_get_option(suite) -> - []; +invalid_inet_get_option() -> + [{doc,"Test handling of invalid inet options in getopts"}]. invalid_inet_get_option(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -923,24 +804,16 @@ invalid_inet_get_option(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). - -get_invalid_inet_option(Socket) -> - {error, {eoptions, {inet_option, foo, _}}} = ssl:getopts(Socket, [foo]), - ok. - %%-------------------------------------------------------------------- -invalid_inet_get_option_not_list(doc) -> - ["Test handling of invalid type in getopts"]; - -invalid_inet_get_option_not_list(suite) -> - []; +invalid_inet_get_option_not_list() -> + [{doc,"Test handling of invalid type in getopts"}]. invalid_inet_get_option_not_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -957,7 +830,7 @@ invalid_inet_get_option_not_list(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -966,16 +839,13 @@ invalid_inet_get_option_not_list(Config) when is_list(Config) -> get_invalid_inet_option_not_list(Socket) -> - {error, {eoptions, {inet_options, some_invalid_atom_here}}} + {error, {options, {socket_options, some_invalid_atom_here}}} = ssl:getopts(Socket, some_invalid_atom_here), ok. %%-------------------------------------------------------------------- -invalid_inet_get_option_improper_list(doc) -> - ["Test handling of invalid type in getopts"]; - -invalid_inet_get_option_improper_list(suite) -> - []; +invalid_inet_get_option_improper_list() -> + [{doc,"Test handling of invalid type in getopts"}]. invalid_inet_get_option_improper_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -992,7 +862,7 @@ invalid_inet_get_option_improper_list(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -1001,15 +871,12 @@ invalid_inet_get_option_improper_list(Config) when is_list(Config) -> get_invalid_inet_option_improper_list(Socket) -> - {error, {eoptions, {inet_option, foo,_}}} = ssl:getopts(Socket, [packet | foo]), + {error, {options, {socket_options, foo,_}}} = ssl:getopts(Socket, [packet | foo]), ok. %%-------------------------------------------------------------------- -invalid_inet_set_option(doc) -> - ["Test handling of invalid inet options in setopts"]; - -invalid_inet_set_option(suite) -> - []; +invalid_inet_set_option() -> + [{doc,"Test handling of invalid inet options in setopts"}]. invalid_inet_set_option(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1026,7 +893,7 @@ invalid_inet_set_option(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -1034,17 +901,14 @@ invalid_inet_set_option(Config) when is_list(Config) -> ssl_test_lib:close(Client). set_invalid_inet_option(Socket) -> - {error, {eoptions, {inet_opt, {packet, foo}}}} = ssl:setopts(Socket, [{packet, foo}]), - {error, {eoptions, {inet_opt, {header, foo}}}} = ssl:setopts(Socket, [{header, foo}]), - {error, {eoptions, {inet_opt, {active, foo}}}} = ssl:setopts(Socket, [{active, foo}]), - {error, {eoptions, {inet_opt, {mode, foo}}}} = ssl:setopts(Socket, [{mode, foo}]), + {error, {options, {socket_options, {packet, foo}}}} = ssl:setopts(Socket, [{packet, foo}]), + {error, {options, {socket_options, {header, foo}}}} = ssl:setopts(Socket, [{header, foo}]), + {error, {options, {socket_options, {active, foo}}}} = ssl:setopts(Socket, [{active, foo}]), + {error, {options, {socket_options, {mode, foo}}}} = ssl:setopts(Socket, [{mode, foo}]), ok. %%-------------------------------------------------------------------- -invalid_inet_set_option_not_list(doc) -> - ["Test handling of invalid type in setopts"]; - -invalid_inet_set_option_not_list(suite) -> - []; +invalid_inet_set_option_not_list() -> + [{doc,"Test handling of invalid type in setopts"}]. invalid_inet_set_option_not_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1061,7 +925,7 @@ invalid_inet_set_option_not_list(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -1070,16 +934,13 @@ invalid_inet_set_option_not_list(Config) when is_list(Config) -> set_invalid_inet_option_not_list(Socket) -> - {error, {eoptions, {not_a_proplist, some_invalid_atom_here}}} + {error, {options, {not_a_proplist, some_invalid_atom_here}}} = ssl:setopts(Socket, some_invalid_atom_here), ok. %%-------------------------------------------------------------------- -invalid_inet_set_option_improper_list(doc) -> - ["Test handling of invalid tye in setopts"]; - -invalid_inet_set_option_improper_list(suite) -> - []; +invalid_inet_set_option_improper_list() -> + [{doc,"Test handling of invalid tye in setopts"}]. invalid_inet_set_option_improper_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1096,7 +957,7 @@ invalid_inet_set_option_improper_list(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -1104,16 +965,13 @@ invalid_inet_set_option_improper_list(Config) when is_list(Config) -> ssl_test_lib:close(Client). set_invalid_inet_option_improper_list(Socket) -> - {error, {eoptions, {not_a_proplist, [{packet, 0} | {foo, 2}]}}} = + {error, {options, {not_a_proplist, [{packet, 0} | {foo, 2}]}}} = ssl:setopts(Socket, [{packet, 0} | {foo, 2}]), ok. %%-------------------------------------------------------------------- -misc_ssl_options(doc) -> - ["Test what happens when we give valid options"]; - -misc_ssl_options(suite) -> - []; +misc_ssl_options() -> + [{doc,"Test what happens when we give valid options"}]. misc_ssl_options(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1125,23 +983,22 @@ misc_ssl_options(Config) when is_list(Config) -> {key, undefined}, {password, []}, {reuse_session, fun(_,_,_,_) -> true end}, - {debug, []}, {cb_info, {gen_tcp, tcp, tcp_closed, tcp_error}}], Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, TestOpts ++ ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, TestOpts ++ ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1149,23 +1006,16 @@ misc_ssl_options(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -versions(doc) -> - ["Test API function versions/0"]; - -versions(suite) -> - []; +versions() -> + [{doc,"Test API function versions/0"}]. versions(Config) when is_list(Config) -> [_|_] = Versions = ssl:versions(), - test_server:format("~p~n", [Versions]). + ct:log("~p~n", [Versions]). %%-------------------------------------------------------------------- -send_recv(doc) -> - [""]; - -send_recv(suite) -> - []; - +send_recv() -> + [{doc,""}]. send_recv(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1173,17 +1023,17 @@ send_recv(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ClientOpts]}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1192,12 +1042,8 @@ send_recv(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -send_close(doc) -> - [""]; - -send_close(suite) -> - []; - +send_close() -> + [{doc,""}]. send_close(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1205,7 +1051,7 @@ send_close(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), {ok, TcpS} = rpc:call(ClientNode, gen_tcp, connect, @@ -1213,7 +1059,7 @@ send_close(Config) when is_list(Config) -> {ok, SslS} = rpc:call(ClientNode, ssl, connect, [TcpS,[{active, false}|ClientOpts]]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), self(), Server]), ok = ssl:send(SslS, "Hello world"), {ok,<<"Hello world">>} = ssl:recv(SslS, 11), @@ -1221,11 +1067,8 @@ send_close(Config) when is_list(Config) -> {error, _} = ssl:send(SslS, "Hello world"). %%-------------------------------------------------------------------- -close_transport_accept(doc) -> - ["Tests closing ssl socket when waiting on ssl:transport_accept/1"]; - -close_transport_accept(suite) -> - []; +close_transport_accept() -> + [{doc,"Tests closing ssl socket when waiting on ssl:transport_accept/1"}]. close_transport_accept(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), @@ -1235,7 +1078,7 @@ close_transport_accept(Config) when is_list(Config) -> Opts = [{active, false} | ServerOpts], {ok, ListenSocket} = rpc:call(ServerNode, ssl, listen, [Port, Opts]), spawn_link(fun() -> - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), rpc:call(ServerNode, ssl, close, [ListenSocket]) end), case rpc:call(ServerNode, ssl, transport_accept, [ListenSocket]) of @@ -1246,11 +1089,8 @@ close_transport_accept(Config) when is_list(Config) -> end. %%-------------------------------------------------------------------- -dh_params(doc) -> - ["Test to specify DH-params file in server."]; - -dh_params(suite) -> - []; +dh_params() -> + [{doc,"Test to specify DH-params file in server."}]. dh_params(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1262,13 +1102,13 @@ dh_params(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, [{dhfile, DHParamFile} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, [{ciphers,[{dhe_rsa,aes_256_cbc,sha,ignore}]} | ClientOpts]}]), @@ -1279,11 +1119,8 @@ dh_params(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -upgrade(doc) -> - ["Test that you can upgrade an tcp connection to an ssl connection"]; - -upgrade(suite) -> - []; +upgrade() -> + [{doc,"Test that you can upgrade an tcp connection to an ssl connection"}]. upgrade(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1307,7 +1144,7 @@ upgrade(Config) when is_list(Config) -> {tcp_options, TcpOpts}, {ssl_options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1331,11 +1168,8 @@ upgrade_result(Socket) -> end. %%-------------------------------------------------------------------- -upgrade_with_timeout(doc) -> - ["Test ssl_accept/3"]; - -upgrade_with_timeout(suite) -> - []; +upgrade_with_timeout() -> + [{doc,"Test ssl_accept/3"}]. upgrade_with_timeout(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1360,7 +1194,7 @@ upgrade_with_timeout(Config) when is_list(Config) -> {tcp_options, TcpOpts}, {ssl_options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1369,58 +1203,52 @@ upgrade_with_timeout(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -tcp_connect(doc) -> - ["Test what happens when a tcp tries to connect, i,e. a bad (ssl) packet is sent first"]; - -tcp_connect(suite) -> - []; +tcp_connect() -> + [{doc,"Test what happens when a tcp tries to connect, i,e. a bad (ssl) packet is sent first"}]. tcp_connect(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), TcpOpts = [binary, {reuseaddr, true}], - Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {timeout, 5000}, - {mfa, {?MODULE, dummy, []}}, - {tcp_options, TcpOpts}, - {ssl_options, ServerOpts}]), + Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {timeout, 5000}, + {mfa, {?MODULE, dummy, []}}, + {tcp_options, TcpOpts}, + {ssl_options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]), - test_server:format("Testcase ~p connected to Server ~p ~n", [self(), Server]), + ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]), gen_tcp:send(Socket, "<SOME GARBLED NON SSL MESSAGE>"), receive {tcp_closed, Socket} -> receive {Server, {error, Error}} -> - test_server:format("Error ~p", [Error]) + ct:log("Error ~p", [Error]) end end. - -tcp_connect_big(doc) -> - ["Test what happens when a tcp tries to connect, i,e. a bad big (ssl) packet is sent first"]; - -tcp_connect_big(suite) -> - []; +%%-------------------------------------------------------------------- +tcp_connect_big() -> + [{doc,"Test what happens when a tcp tries to connect, i,e. a bad big (ssl) packet is sent first"}]. tcp_connect_big(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), TcpOpts = [binary, {reuseaddr, true}], - Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {timeout, 5000}, - {mfa, {?MODULE, dummy, []}}, - {tcp_options, TcpOpts}, - {ssl_options, ServerOpts}]), + Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {timeout, 5000}, + {mfa, {?MODULE, dummy, []}}, + {tcp_options, TcpOpts}, + {ssl_options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]), - test_server:format("Testcase ~p connected to Server ~p ~n", [self(), Server]), + ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]), Rand = crypto:rand_bytes(?MAX_CIPHER_TEXT_LENGTH+1), gen_tcp:send(Socket, <<?BYTE(0), @@ -1430,24 +1258,16 @@ tcp_connect_big(Config) when is_list(Config) -> {tcp_closed, Socket} -> receive {Server, {error, timeout}} -> - test_server:fail("hangs"); + ct:fail("hangs"); {Server, {error, Error}} -> - test_server:format("Error ~p", [Error]) + ct:log("Error ~p", [Error]) end end. -dummy(_Socket) -> - %% Should not happen as the ssl connection will not be established - %% due to fatal handshake failiure - exit(kill). - %%-------------------------------------------------------------------- ipv6() -> - [{require, ipv6_hosts}]. -ipv6(doc) -> - ["Test ipv6."]; -ipv6(suite) -> - []; + [{require, ipv6_hosts}, + {doc,"Test ipv6."}]. ipv6(Config) when is_list(Config) -> {ok, Hostname0} = inet:gethostname(), @@ -1459,18 +1279,18 @@ ipv6(Config) when is_list(Config) -> ssl_test_lib:run_where(Config, ipv6), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [inet6, {active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [inet6, {active, false} | ClientOpts]}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1483,13 +1303,9 @@ ipv6(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -ekeyfile(doc) -> - ["Test what happens with an invalid key file"]; - -ekeyfile(suite) -> - []; - -ekeyfile(Config) when is_list(Config) -> +invalid_keyfile() -> + [{doc,"Test what happens with an invalid key file"}]. +invalid_keyfile(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), BadOpts = ?config(server_bad_key, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1505,19 +1321,17 @@ ekeyfile(Config) when is_list(Config) -> ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {options, ClientOpts}]), - - ssl_test_lib:check_result(Server, {error, ekeyfile}, Client, - {error, closed}). -%%-------------------------------------------------------------------- + File = proplists:get_value(keyfile,BadOpts), + ssl_test_lib:check_result(Server, {error,{options, {keyfile, File, {error,enoent}}}}, Client, + {error, closed}). -ecertfile(doc) -> - ["Test what happens with an invalid cert file"]; +%%-------------------------------------------------------------------- -ecertfile(suite) -> - []; +invalid_certfile() -> + [{doc,"Test what happens with an invalid cert file"}]. -ecertfile(Config) when is_list(Config) -> +invalid_certfile(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerBadOpts = ?config(server_bad_cert, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1534,19 +1348,16 @@ ecertfile(Config) when is_list(Config) -> {port, Port}, {host, Hostname}, {from, self()}, {options, ClientOpts}]), - - ssl_test_lib:check_result(Server, {error, ecertfile}, Client, - {error, closed}). + File = proplists:get_value(certfile, ServerBadOpts), + ssl_test_lib:check_result(Server, {error,{options, {certfile, File, {error,enoent}}}}, + Client, {error, closed}). %%-------------------------------------------------------------------- -ecacertfile(doc) -> - ["Test what happens with an invalid cacert file"]; - -ecacertfile(suite) -> - []; +invalid_cacertfile() -> + [{doc,"Test what happens with an invalid cacert file"}]. -ecacertfile(Config) when is_list(Config) -> +invalid_cacertfile(Config) when is_list(Config) -> ClientOpts = [{reuseaddr, true}|?config(client_opts, Config)], ServerBadOpts = [{reuseaddr, true}|?config(server_bad_ca, Config)], {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1564,11 +1375,12 @@ ecacertfile(Config) when is_list(Config) -> {port, Port0}, {host, Hostname}, {from, self()}, {options, ClientOpts}]), + + File0 = proplists:get_value(cacertfile, ServerBadOpts), - ssl_test_lib:check_result(Server0, {error, ecacertfile}, + ssl_test_lib:check_result(Server0, {error, {options, {cacertfile, File0,{error,enoent}}}}, Client0, {error, closed}), - File0 = proplists:get_value(cacertfile, ServerBadOpts), File = File0 ++ "do_not_exit.pem", ServerBadOpts1 = [{cacertfile, File}|proplists:delete(cacertfile, ServerBadOpts)], @@ -1585,34 +1397,32 @@ ecacertfile(Config) when is_list(Config) -> {from, self()}, {options, ClientOpts}]), - ssl_test_lib:check_result(Server1, {error, ecacertfile}, + + ssl_test_lib:check_result(Server1, {error, {options, {cacertfile, File,{error,enoent}}}}, Client1, {error, closed}), ok. %%-------------------------------------------------------------------- -eoptions(doc) -> - ["Test what happens when we give invalid options"]; +invalid_options() -> + [{doc,"Test what happens when we give invalid options"}]. -eoptions(suite) -> - []; - -eoptions(Config) when is_list(Config) -> +invalid_options(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Check = fun(Client, Server, {versions, [sslv2, sslv3]} = Option) -> ssl_test_lib:check_result(Server, - {error, {eoptions, {sslv2, Option}}}, + {error, {options, {sslv2, Option}}}, Client, - {error, {eoptions, {sslv2, Option}}}); + {error, {options, {sslv2, Option}}}); (Client, Server, Option) -> ssl_test_lib:check_result(Server, - {error, {eoptions, Option}}, + {error, {options, Option}}, Client, - {error, {eoptions, Option}}) + {error, {options, Option}}) end, TestOpts = [{versions, [sslv2, sslv3]}, @@ -1630,7 +1440,6 @@ eoptions(Config) when is_list(Config) -> {reuse_session, foo}, {reuse_sessions, 0}, {renegotiate_at, "10"}, - {debug, 1}, {mode, depech}, {packet, 8.0}, {packet_size, "2"}, @@ -1654,12 +1463,8 @@ eoptions(Config) when is_list(Config) -> ok. %%-------------------------------------------------------------------- -shutdown(doc) -> - [""]; - -shutdown(suite) -> - []; - +shutdown() -> + [{doc,"Test API function ssl:shutdown/2"}]. shutdown(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1684,25 +1489,9 @@ shutdown(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). -shutdown_result(Socket, server) -> - ssl:send(Socket, "Hej"), - ssl:shutdown(Socket, write), - {ok, "Hej hopp"} = ssl:recv(Socket, 8), - ok; - -shutdown_result(Socket, client) -> - {ok, "Hej"} = ssl:recv(Socket, 3), - ssl:send(Socket, "Hej hopp"), - ssl:shutdown(Socket, write), - ok. - %%-------------------------------------------------------------------- -shutdown_write(doc) -> - [""]; - -shutdown_write(suite) -> - []; - +shutdown_write() -> + [{doc,"Test API function ssl:shutdown/2 with option write."}]. shutdown_write(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1719,20 +1508,10 @@ shutdown_write(Config) when is_list(Config) -> {options, [{active, false} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, {error, closed}). - -shutdown_write_result(Socket, server) -> - test_server:sleep(?SLEEP), - ssl:shutdown(Socket, write); -shutdown_write_result(Socket, client) -> - ssl:recv(Socket, 0). %%-------------------------------------------------------------------- -shutdown_both(doc) -> - [""]; - -shutdown_both(suite) -> - []; - +shutdown_both() -> + [{doc,"Test API function ssl:shutdown/2 with option both."}]. shutdown_both(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1750,19 +1529,9 @@ shutdown_both(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, {error, closed}). -shutdown_both_result(Socket, server) -> - test_server:sleep(?SLEEP), - ssl:shutdown(Socket, read_write); -shutdown_both_result(Socket, client) -> - ssl:recv(Socket, 0). - %%-------------------------------------------------------------------- -shutdown_error(doc) -> - [""]; - -shutdown_error(suite) -> - []; - +shutdown_error() -> + [{doc,"Test ssl:shutdown/2 error handling"}]. shutdown_error(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), Port = ssl_test_lib:inet_port(node()), @@ -1772,141 +1541,108 @@ shutdown_error(Config) when is_list(Config) -> {error, closed} = ssl:shutdown(Listen, read_write). %%------------------------------------------------------------------- -ciphers_rsa_signed_certs(doc) -> - ["Test all rsa ssl cipher suites in highest support ssl/tls version"]; +ciphers_rsa_signed_certs() -> + [{doc,"Test all rsa ssl cipher suites in highest support ssl/tls version"}]. -ciphers_rsa_signed_certs(suite) -> - []; - ciphers_rsa_signed_certs(Config) when is_list(Config) -> Version = - ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + tls_record:protocol_version(tls_record:highest_protocol_version([])), - Ciphers = ssl_test_lib:rsa_suites(), - test_server:format("~p erlang cipher suites ~p~n", [Version, Ciphers]), + Ciphers = ssl_test_lib:rsa_suites(crypto), + ct:log("~p erlang cipher suites ~p~n", [Version, Ciphers]), run_suites(Ciphers, Version, Config, rsa). - -ciphers_rsa_signed_certs_openssl_names(doc) -> - ["Test all rsa ssl cipher suites in highest support ssl/tls version"]; +%%------------------------------------------------------------------- +ciphers_rsa_signed_certs_openssl_names() -> + [{doc,"Test all rsa ssl cipher suites in highest support ssl/tls version"}]. -ciphers_rsa_signed_certs_openssl_names(suite) -> - []; - ciphers_rsa_signed_certs_openssl_names(Config) when is_list(Config) -> Version = - ssl_record:protocol_version(ssl_record:highest_protocol_version([])), - Ciphers = ssl_test_lib:openssl_rsa_suites(), - test_server:format("tls1 openssl cipher suites ~p~n", [Ciphers]), + tls_record:protocol_version(tls_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:openssl_rsa_suites(crypto), + ct:log("tls1 openssl cipher suites ~p~n", [Ciphers]), run_suites(Ciphers, Version, Config, rsa). - -ciphers_dsa_signed_certs(doc) -> - ["Test all dsa ssl cipher suites in highest support ssl/tls version"]; +%%------------------------------------------------------------------- +ciphers_dsa_signed_certs() -> + [{doc,"Test all dsa ssl cipher suites in highest support ssl/tls version"}]. -ciphers_dsa_signed_certs(suite) -> - []; - ciphers_dsa_signed_certs(Config) when is_list(Config) -> Version = - ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + tls_record:protocol_version(tls_record:highest_protocol_version([])), Ciphers = ssl_test_lib:dsa_suites(), - test_server:format("~p erlang cipher suites ~p~n", [Version, Ciphers]), + ct:log("~p erlang cipher suites ~p~n", [Version, Ciphers]), run_suites(Ciphers, Version, Config, dsa). - -ciphers_dsa_signed_certs_openssl_names(doc) -> - ["Test all dsa ssl cipher suites in highest support ssl/tls version"]; +%%------------------------------------------------------------------- +ciphers_dsa_signed_certs_openssl_names() -> + [{doc,"Test all dsa ssl cipher suites in highest support ssl/tls version"}]. -ciphers_dsa_signed_certs_openssl_names(suite) -> - []; - ciphers_dsa_signed_certs_openssl_names(Config) when is_list(Config) -> Version = - ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + tls_record:protocol_version(tls_record:highest_protocol_version([])), Ciphers = ssl_test_lib:openssl_dsa_suites(), - test_server:format("tls1 openssl cipher suites ~p~n", [Ciphers]), + ct:log("tls1 openssl cipher suites ~p~n", [Ciphers]), run_suites(Ciphers, Version, Config, dsa). - -anonymous_cipher_suites(doc)-> - ["Test the anonymous ciphersuites"]; -anonymous_cipher_suites(suite) -> - []; +%%------------------------------------------------------------------- +anonymous_cipher_suites()-> + [{doc,"Test the anonymous ciphersuites"}]. anonymous_cipher_suites(Config) when is_list(Config) -> - Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Ciphers = ssl_test_lib:anonymous_suites(), run_suites(Ciphers, Version, Config, anonymous). - -run_suites(Ciphers, Version, Config, Type) -> - {ClientOpts, ServerOpts} = - case Type of - rsa -> - {?config(client_opts, Config), - ?config(server_opts, Config)}; - dsa -> - {?config(client_opts, Config), - ?config(server_dsa_opts, Config)}; - anonymous -> - %% No certs in opts! - {?config(client_opts, Config), - ?config(server_anon, Config)} - end, - - Result = lists:map(fun(Cipher) -> - cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end, - Ciphers), - case lists:flatten(Result) of - [] -> - ok; - Error -> - test_server:format("Cipher suite errors: ~p~n", [Error]), - test_server:fail(cipher_suite_failed_see_test_case_log) - end. - -erlang_cipher_suite(Suite) when is_list(Suite)-> - ssl:suite_definition(ssl_cipher:openssl_suite(Suite)); -erlang_cipher_suite(Suite) -> - Suite. - -cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> - %% process_flag(trap_exit, true), - test_server:format("Testing CipherSuite ~p~n", [CipherSuite]), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - ErlangCipherSuite = erlang_cipher_suite(CipherSuite), - - ConnectionInfo = {ok, {Version, ErlangCipherSuite}}, - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, - {options, - [{ciphers,[CipherSuite]} | - ClientOpts]}]), - - Result = ssl_test_lib:wait_for_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - - case Result of - ok -> - []; - Error -> - [{ErlangCipherSuite, Error}] - end. - -%%-------------------------------------------------------------------- -default_reject_anonymous(doc)-> - ["Test that by default anonymous cipher suites are rejected "]; -default_reject_anonymous(suite) -> - []; +%%------------------------------------------------------------------- +psk_cipher_suites() -> + [{doc, "Test the PSK ciphersuites WITHOUT server supplied identity hint"}]. +psk_cipher_suites(Config) when is_list(Config) -> + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:psk_suites(), + run_suites(Ciphers, Version, Config, psk). +%%------------------------------------------------------------------- +psk_with_hint_cipher_suites()-> + [{doc, "Test the PSK ciphersuites WITH server supplied identity hint"}]. +psk_with_hint_cipher_suites(Config) when is_list(Config) -> + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:psk_suites(), + run_suites(Ciphers, Version, Config, psk_with_hint). +%%------------------------------------------------------------------- +psk_anon_cipher_suites() -> + [{doc, "Test the anonymous PSK ciphersuites WITHOUT server supplied identity hint"}]. +psk_anon_cipher_suites(Config) when is_list(Config) -> + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:psk_anon_suites(), + run_suites(Ciphers, Version, Config, psk_anon). +%%------------------------------------------------------------------- +psk_anon_with_hint_cipher_suites()-> + [{doc, "Test the anonymous PSK ciphersuites WITH server supplied identity hint"}]. +psk_anon_with_hint_cipher_suites(Config) when is_list(Config) -> + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:psk_anon_suites(), + run_suites(Ciphers, Version, Config, psk_anon_with_hint). +%%------------------------------------------------------------------- +srp_cipher_suites()-> + [{doc, "Test the SRP ciphersuites"}]. +srp_cipher_suites(Config) when is_list(Config) -> + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:srp_suites(), + run_suites(Ciphers, Version, Config, srp). +%%------------------------------------------------------------------- +srp_anon_cipher_suites()-> + [{doc, "Test the anonymous SRP ciphersuites"}]. +srp_anon_cipher_suites(Config) when is_list(Config) -> + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:srp_anon_suites(), + run_suites(Ciphers, Version, Config, srp_anon). +%%------------------------------------------------------------------- +srp_dsa_cipher_suites()-> + [{doc, "Test the SRP DSA ciphersuites"}]. +srp_dsa_cipher_suites(Config) when is_list(Config) -> + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:srp_dss_suites(), + run_suites(Ciphers, Version, Config, srp_dsa). +%%-------------------------------------------------------------------- +default_reject_anonymous()-> + [{doc,"Test that by default anonymous cipher suites are rejected "}]. default_reject_anonymous(Config) when is_list(Config) -> {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), ClientOpts = ?config(client_opts, Config), @@ -1925,16 +1661,54 @@ default_reject_anonymous(Config) when is_list(Config) -> [{ciphers,[Cipher]} | ClientOpts]}]), - ssl_test_lib:check_result(Server, {error, "insufficient security"}, - Client, {error, "insufficient security"}). + ssl_test_lib:check_result(Server, {error, {tls_alert, "insufficient security"}}, + Client, {error, {tls_alert, "insufficient security"}}). %%-------------------------------------------------------------------- -reuse_session(doc) -> - ["Test reuse of sessions (short handshake)"]; +ciphers_ecdsa_signed_certs() -> + [{doc, "Test all ecdsa ssl cipher suites in highest support ssl/tls version"}]. + +ciphers_ecdsa_signed_certs(Config) when is_list(Config) -> + Version = + tls_record:protocol_version(tls_record:highest_protocol_version([])), + + Ciphers = ssl_test_lib:ecdsa_suites(), + ct:log("~p erlang cipher suites ~p~n", [Version, Ciphers]), + run_suites(Ciphers, Version, Config, ecdsa). +%%-------------------------------------------------------------------- +ciphers_ecdsa_signed_certs_openssl_names() -> + [{doc, "Test all ecdsa ssl cipher suites in highest support ssl/tls version"}]. + +ciphers_ecdsa_signed_certs_openssl_names(Config) when is_list(Config) -> + Version = + tls_record:protocol_version(tls_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:openssl_ecdsa_suites(), + ct:log("tls1 openssl cipher suites ~p~n", [Ciphers]), + run_suites(Ciphers, Version, Config, ecdsa). +%%-------------------------------------------------------------------- +ciphers_ecdh_rsa_signed_certs() -> + [{doc, "Test all ecdh_rsa ssl cipher suites in highest support ssl/tls version"}]. + +ciphers_ecdh_rsa_signed_certs(Config) when is_list(Config) -> + Version = + tls_record:protocol_version(tls_record:highest_protocol_version([])), -reuse_session(suite) -> - []; + Ciphers = ssl_test_lib:ecdh_rsa_suites(), + ct:log("~p erlang cipher suites ~p~n", [Version, Ciphers]), + run_suites(Ciphers, Version, Config, ecdh_rsa). +%%-------------------------------------------------------------------- +ciphers_ecdh_rsa_signed_certs_openssl_names() -> + [{doc, "Test all ecdh_rsa ssl cipher suites in highest support ssl/tls version"}]. +ciphers_ecdh_rsa_signed_certs_openssl_names(Config) when is_list(Config) -> + Version = + tls_record:protocol_version(tls_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:openssl_ecdh_rsa_suites(), + ct:log("tls1 openssl cipher suites ~p~n", [Ciphers]), + run_suites(Ciphers, Version, Config, ecdh_rsa). +%%-------------------------------------------------------------------- +reuse_session() -> + [{doc,"Test reuse of sessions (short handshake)"}]. reuse_session(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1960,7 +1734,7 @@ reuse_session(Config) when is_list(Config) -> Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), Client1 = ssl_test_lib:start_client([{node, ClientNode}, @@ -1971,9 +1745,9 @@ reuse_session(Config) when is_list(Config) -> {Client1, SessionInfo} -> ok; {Client1, Other} -> - test_server:format("Expected: ~p, Unexpected: ~p~n", + ct:log("Expected: ~p, Unexpected: ~p~n", [SessionInfo, Other]), - test_server:fail(session_not_reused) + ct:fail(session_not_reused) end, Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, @@ -1986,7 +1760,7 @@ reuse_session(Config) when is_list(Config) -> | ClientOpts]}]), receive {Client2, SessionInfo} -> - test_server:fail( + ct:fail( session_reused_when_session_reuse_disabled_by_client); {Client2, _} -> ok @@ -2016,7 +1790,7 @@ reuse_session(Config) when is_list(Config) -> Server1 ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), Client4 = ssl_test_lib:start_client([{node, ClientNode}, @@ -2026,10 +1800,10 @@ reuse_session(Config) when is_list(Config) -> receive {Client4, SessionInfo1} -> - test_server:fail( + ct:fail( session_reused_when_session_reuse_disabled_by_server); {Client4, _Other} -> - test_server:format("OTHER: ~p ~n", [_Other]), + ct:log("OTHER: ~p ~n", [_Other]), ok end, @@ -2041,12 +1815,8 @@ reuse_session(Config) when is_list(Config) -> ssl_test_lib:close(Client4). %%-------------------------------------------------------------------- -reuse_session_expired(doc) -> - ["Test sessions is not reused when it has expired"]; - -reuse_session_expired(suite) -> - []; - +reuse_session_expired() -> + [{doc,"Test sessions is not reused when it has expired"}]. reuse_session_expired(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2072,7 +1842,7 @@ reuse_session_expired(Config) when is_list(Config) -> Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), Client1 = ssl_test_lib:start_client([{node, ClientNode}, @@ -2083,15 +1853,15 @@ reuse_session_expired(Config) when is_list(Config) -> {Client1, SessionInfo} -> ok; {Client1, Other} -> - test_server:format("Expected: ~p, Unexpected: ~p~n", + ct:log("Expected: ~p, Unexpected: ~p~n", [SessionInfo, Other]), - test_server:fail(session_not_reused) + ct:fail(session_not_reused) end, Server ! listen, %% Make sure session is unregistered due to expiration - test_server:sleep((?EXPIRE+1)), + ct:sleep((?EXPIRE+1)), [{session_id, Id} |_] = SessionInfo, make_sure_expired(Hostname, Port, Id), @@ -2103,7 +1873,7 @@ reuse_session_expired(Config) when is_list(Config) -> {from, self()}, {options, ClientOpts}]), receive {Client2, SessionInfo} -> - test_server:fail(session_reused_when_session_expired); + ct:fail(session_reused_when_session_expired); {Client2, _} -> ok end, @@ -2125,17 +1895,13 @@ make_sure_expired(Host, Port, Id) -> #session{is_resumable = false} -> ok; _ -> - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), make_sure_expired(Host, Port, Id) end. %%-------------------------------------------------------------------- -server_does_not_want_to_reuse_session(doc) -> - ["Test reuse of sessions (short handshake)"]; - -server_does_not_want_to_reuse_session(suite) -> - []; - +server_does_not_want_to_reuse_session() -> + [{doc,"Test reuse of sessions (short handshake)"}]. server_does_not_want_to_reuse_session(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2164,7 +1930,7 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) -> Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), ssl_test_lib:close(Client0), Client1 = @@ -2174,7 +1940,7 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) -> {from, self()}, {options, ClientOpts}]), receive {Client1, SessionInfo} -> - test_server:fail(session_reused_when_server_does_not_want_to); + ct:fail(session_reused_when_server_does_not_want_to); {Client1, _Other} -> ok end, @@ -2183,512 +1949,55 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) -> ssl_test_lib:close(Client1). %%-------------------------------------------------------------------- - -server_verify_peer_passive(doc) -> - ["Test server option verify_peer"]; - -server_verify_peer_passive(suite) -> - []; - -server_verify_peer_passive(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false}, {verify, verify_peer} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -server_verify_peer_active(doc) -> - ["Test server option verify_peer"]; - -server_verify_peer_active(suite) -> - []; - -server_verify_peer_active(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{active, true}, {verify, verify_peer} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{active, true} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -server_verify_peer_active_once(doc) -> - ["Test server option verify_peer"]; - -server_verify_peer_active_once(suite) -> - []; - -server_verify_peer_active_once(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active_once, []}}, - {options, [{active, once}, {verify, verify_peer} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active_once, []}}, - {options, [{active, once} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -server_verify_none_passive(doc) -> - ["Test server option verify_none"]; - -server_verify_none_passive(suite) -> - []; - -server_verify_none_passive(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false}, {verify, verify_none} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -server_verify_none_active(doc) -> - ["Test server option verify_none"]; - -server_verify_none_active(suite) -> - []; - -server_verify_none_active(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{active, true}, {verify, verify_none} | - ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{active, true} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -server_verify_none_active_once(doc) -> - ["Test server option verify_none"]; - -server_verify_none_active_once(suite) -> - []; - -server_verify_none_active_once(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active_once, []}}, - {options, [{active, once}, {verify, verify_none} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active_once, []}}, - {options, [{active, once} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -server_verify_client_once_passive(doc) -> - ["Test server option verify_client_once"]; - -server_verify_client_once_passive(suite) -> - []; - -server_verify_client_once_passive(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false}, {verify, verify_peer}, - {verify_client_once, true} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client0, ok), - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - ssl_test_lib:close(Client0), - Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, result_ok, []}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Client1, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client1). - -%%-------------------------------------------------------------------- - -server_verify_client_once_active(doc) -> - ["Test server option verify_client_once"]; - -server_verify_client_once_active(suite) -> - []; - -server_verify_client_once_active(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{active, true}, {verify, verify_peer}, - {verify_client_once, true} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{active, true} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client0, ok), - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - ssl_test_lib:close(Client0), - Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, result_ok, []}}, - {options, [{active, true} | ClientOpts]}]), - - ssl_test_lib:check_result(Client1, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client1). - -%%-------------------------------------------------------------------- - -server_verify_client_once_active_once(doc) -> - ["Test server option verify_client_once"]; - -server_verify_client_once_active_once(suite) -> - []; - -server_verify_client_once_active_once(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active_once, []}}, - {options, [{active, once}, {verify, verify_peer}, - {verify_client_once, true} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active_once, []}}, - {options, [{active, once} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client0, ok), - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - ssl_test_lib:close(Client0), - Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, result_ok, []}}, - {options, [{active, once} | ClientOpts]}]), - - ssl_test_lib:check_result(Client1, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client1). - -%%-------------------------------------------------------------------- - -server_verify_no_cacerts(doc) -> - ["Test server must have cacerts if it wants to verify client"]; - -server_verify_no_cacerts(suite) -> - []; -server_verify_no_cacerts(Config) when is_list(Config) -> - ServerOpts = ?config(server_opts, Config), - {_, ServerNode, _} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, [{verify, verify_peer} - | ServerOpts]}]), - - ssl_test_lib:check_result(Server, {error, {eoptions, {cacertfile, ""}}}). - -%%-------------------------------------------------------------------- - -server_require_peer_cert_ok(doc) -> - ["Test server option fail_if_no_peer_cert when peer sends cert"]; - -server_require_peer_cert_ok(suite) -> - []; - -server_require_peer_cert_ok(Config) when is_list(Config) -> - ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} - | ?config(server_verification_opts, Config)], - ClientOpts = ?config(client_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -server_require_peer_cert_fail(doc) -> - ["Test server option fail_if_no_peer_cert when peer doesn't send cert"]; - -server_require_peer_cert_fail(suite) -> - []; - -server_require_peer_cert_fail(Config) when is_list(Config) -> - ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} - | ?config(server_verification_opts, Config)], - BadClientOpts = ?config(client_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, [{active, false} | ServerOpts]}]), - - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{active, false} | BadClientOpts]}]), - - ssl_test_lib:check_result(Server, {error, esslaccept}, - Client, {error, esslconnect}). - -%%-------------------------------------------------------------------- - -client_verify_none_passive(doc) -> - ["Test client option verify_none"]; - -client_verify_none_passive(suite) -> - []; - -client_verify_none_passive(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false}, - {verify, verify_none} - | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -client_verify_none_active(doc) -> - ["Test client option verify_none"]; - -client_verify_none_active(suite) -> - []; - -client_verify_none_active(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, [{active, true} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, [{active, true}, - {verify, verify_none} - | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -client_verify_none_active_once(doc) -> - ["Test client option verify_none"]; - -client_verify_none_active_once(suite) -> - []; - -client_verify_none_active_once(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active_once, []}}, - {options, [{active, once} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active_once, - []}}, - {options, [{active, once}, - {verify, verify_none} - | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -client_renegotiate(doc) -> - ["Test ssl:renegotiate/1 on client."]; - -client_renegotiate(suite) -> - []; - +client_renegotiate() -> + [{doc,"Test ssl:renegotiate/1 on client."}]. client_renegotiate(Config) when is_list(Config) -> - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - + Data = "From erlang to erlang", - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, + {from, self()}, + {mfa, {?MODULE, renegotiate, [Data]}}, {options, [{reuse_sessions, false} | ClientOpts]}]), - ssl_test_lib:check_result(Client, ok, Server, ok), + ssl_test_lib:check_result(Client, ok, Server, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -server_renegotiate(doc) -> - ["Test ssl:renegotiate/1 on server."]; - -server_renegotiate(suite) -> - []; - +server_renegotiate() -> + [{doc,"Test ssl:renegotiate/1 on server."}]. server_renegotiate(Config) when is_list(Config) -> - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - + Data = "From erlang to erlang", Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, + {mfa, {?MODULE, renegotiate, [Data]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, + {from, self()}, {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, [{reuse_sessions, false} | ClientOpts]}]), @@ -2697,805 +2006,133 @@ server_renegotiate(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -client_renegotiate_reused_session(doc) -> - ["Test ssl:renegotiate/1 on client when the ssl session will be reused."]; - -client_renegotiate_reused_session(suite) -> - []; - +client_renegotiate_reused_session() -> + [{doc,"Test ssl:renegotiate/1 on client when the ssl session will be reused."}]. client_renegotiate_reused_session(Config) when is_list(Config) -> - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - + Data = "From erlang to erlang", - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, + {from, self()}, + {mfa, {?MODULE, renegotiate_reuse_session, [Data]}}, {options, [{reuse_sessions, true} | ClientOpts]}]), - ssl_test_lib:check_result(Client, ok, Server, ok), + ssl_test_lib:check_result(Client, ok, Server, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -server_renegotiate_reused_session(doc) -> - ["Test ssl:renegotiate/1 on server when the ssl session will be reused."]; - -server_renegotiate_reused_session(suite) -> - []; - +server_renegotiate_reused_session() -> + [{doc,"Test ssl:renegotiate/1 on server when the ssl session will be reused."}]. server_renegotiate_reused_session(Config) when is_list(Config) -> - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Data = "From erlang to erlang", - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, renegotiate_reuse_session, [Data]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, + {from, self()}, {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, [{reuse_sessions, true} | ClientOpts]}]), - + ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -client_no_wrap_sequence_number(doc) -> - ["Test that erlang client will renegotiate session when", +client_no_wrap_sequence_number() -> + [{doc,"Test that erlang client will renegotiate session when", "max sequence number celing is about to be reached. Although" - "in the testcase we use the test option renegotiate_at" - " to lower treashold substantially."]; - -client_no_wrap_sequence_number(suite) -> - []; + "in the testcase we use the test option renegotiate_at" + " to lower treashold substantially."}]. client_no_wrap_sequence_number(Config) when is_list(Config) -> - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - + ErlData = "From erlang to erlang", N = 10, - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {ssl_test_lib, no_result, []}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - - Version = ssl_record:highest_protocol_version(ssl_record:supported_protocol_versions()), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + Version = tls_record:highest_protocol_version(tls_record:supported_protocol_versions()), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, + {from, self()}, + {mfa, {ssl_test_lib, trigger_renegotiate, [[ErlData, treashold(N, Version)]]}}, {options, [{reuse_sessions, false}, {renegotiate_at, N} | ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), + + ssl_test_lib:check_result(Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). - %% First two clauses handles 1/n-1 splitting countermeasure Rizzo/Duong-Beast -treashold(N, {3,0}) -> - (N div 2) + 1; -treashold(N, {3,1}) -> - (N div 2) + 1; -treashold(N, _) -> - N + 1. - %%-------------------------------------------------------------------- -server_no_wrap_sequence_number(doc) -> - ["Test that erlang server will renegotiate session when", +server_no_wrap_sequence_number() -> + [{doc, "Test that erlang server will renegotiate session when", "max sequence number celing is about to be reached. Although" - "in the testcase we use the test option renegotiate_at" - " to lower treashold substantially."]; - -server_no_wrap_sequence_number(suite) -> - []; + "in the testcase we use the test option renegotiate_at" + " to lower treashold substantially."}]. server_no_wrap_sequence_number(Config) when is_list(Config) -> - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From erlang to erlang", - N = 10, - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - trigger_renegotiate, [[Data, N+2]]}}, - {options, [{renegotiate_at, N} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [{reuse_sessions, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). -%%-------------------------------------------------------------------- -extended_key_usage_verify_peer(doc) -> - ["Test cert that has a critical extended_key_usage extension in verify_peer mode"]; - -extended_key_usage_verify_peer(suite) -> - []; - -extended_key_usage_verify_peer(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - PrivDir = ?config(priv_dir, Config), - - KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), - [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), - Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), - - ServerCertFile = proplists:get_value(certfile, ServerOpts), - NewServerCertFile = filename:join(PrivDir, "server/new_cert.pem"), - [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), - ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), - ServerExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']}, - ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, - ServerExtensions = ServerOTPTbsCert#'OTPTBSCertificate'.extensions, - NewServerOTPTbsCert = ServerOTPTbsCert#'OTPTBSCertificate'{extensions = - [ServerExtKeyUsageExt | - ServerExtensions]}, - NewServerDerCert = public_key:pkix_sign(NewServerOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), - NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], - - ClientCertFile = proplists:get_value(certfile, ClientOpts), - NewClientCertFile = filename:join(PrivDir, "client/new_cert.pem"), - [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), - ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), - ClientExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-clientAuth']}, - ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, - ClientExtensions = ClientOTPTbsCert#'OTPTBSCertificate'.extensions, - NewClientOTPTbsCert = ClientOTPTbsCert#'OTPTBSCertificate'{extensions = - [ClientExtKeyUsageExt | - ClientExtensions]}, - NewClientDerCert = public_key:pkix_sign(NewClientOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), - NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{verify, verify_peer} | NewServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{verify, verify_peer} | NewClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -extended_key_usage_verify_none(doc) -> - ["Test cert that has a critical extended_key_usage extension in verify_none mode"]; - -extended_key_usage_verify_none(suite) -> - []; - -extended_key_usage_verify_none(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - PrivDir = ?config(priv_dir, Config), - - KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), - [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), - Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), - - ServerCertFile = proplists:get_value(certfile, ServerOpts), - NewServerCertFile = filename:join(PrivDir, "server/new_cert.pem"), - [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), - ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), - ServerExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']}, - ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, - ServerExtensions = ServerOTPTbsCert#'OTPTBSCertificate'.extensions, - NewServerOTPTbsCert = ServerOTPTbsCert#'OTPTBSCertificate'{extensions = - [ServerExtKeyUsageExt | - ServerExtensions]}, - NewServerDerCert = public_key:pkix_sign(NewServerOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), - NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], - - ClientCertFile = proplists:get_value(certfile, ClientOpts), - NewClientCertFile = filename:join(PrivDir, "client/new_cert.pem"), - [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), - ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), - ClientExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-clientAuth']}, - ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, - ClientExtensions = ClientOTPTbsCert#'OTPTBSCertificate'.extensions, - NewClientOTPTbsCert = ClientOTPTbsCert#'OTPTBSCertificate'{extensions = - [ClientExtKeyUsageExt | - ClientExtensions]}, - NewClientDerCert = public_key:pkix_sign(NewClientOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), - NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{verify, verify_none} | NewServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{verify, verify_none} | NewClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -no_authority_key_identifier(doc) -> - ["Test cert that does not have authorityKeyIdentifier extension" - " but are present in trusted certs db."]; - -no_authority_key_identifier(suite) -> - []; -no_authority_key_identifier(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), ServerOpts = ?config(server_opts, Config), - PrivDir = ?config(priv_dir, Config), - - KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), - [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), - Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), - - CertFile = proplists:get_value(certfile, ServerOpts), - NewCertFile = filename:join(PrivDir, "server/new_cert.pem"), - [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(CertFile), - OTPCert = public_key:pkix_decode_cert(DerCert, otp), - OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate, - Extensions = OTPTbsCert#'OTPTBSCertificate'.extensions, - NewExtensions = delete_authority_key_extension(Extensions, []), - NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{extensions = NewExtensions}, - - test_server:format("Extensions ~p~n, NewExtensions: ~p~n", [Extensions, NewExtensions]), - - NewDerCert = public_key:pkix_sign(NewOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewCertFile, [{'Certificate', NewDerCert, not_encrypted}]), - NewServerOpts = [{certfile, NewCertFile} | proplists:delete(certfile, ServerOpts)], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, NewServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{verify, verify_peer} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -delete_authority_key_extension([], Acc) -> - lists:reverse(Acc); -delete_authority_key_extension([#'Extension'{extnID = ?'id-ce-authorityKeyIdentifier'} | Rest], - Acc) -> - delete_authority_key_extension(Rest, Acc); -delete_authority_key_extension([Head | Rest], Acc) -> - delete_authority_key_extension(Rest, [Head | Acc]). - -%%-------------------------------------------------------------------- - -invalid_signature_server(doc) -> - ["Test server with invalid signature"]; - -invalid_signature_server(suite) -> - []; - -invalid_signature_server(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - PrivDir = ?config(priv_dir, Config), - - KeyFile = filename:join(PrivDir, "server/key.pem"), - [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), - Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), - - ServerCertFile = proplists:get_value(certfile, ServerOpts), - NewServerCertFile = filename:join(PrivDir, "server/invalid_cert.pem"), - [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), - ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), - ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, - NewServerDerCert = public_key:pkix_sign(ServerOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), - NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, NewServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{verify, verify_peer} | ClientOpts]}]), - - tcp_delivery_workaround(Server, {error, "bad certificate"}, - Client, {error,"bad certificate"}). - -%%-------------------------------------------------------------------- - -invalid_signature_client(doc) -> - ["Test server with invalid signature"]; - -invalid_signature_client(suite) -> - []; - -invalid_signature_client(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - PrivDir = ?config(priv_dir, Config), - - KeyFile = filename:join(PrivDir, "client/key.pem"), - [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), - Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), - - ClientCertFile = proplists:get_value(certfile, ClientOpts), - NewClientCertFile = filename:join(PrivDir, "client/invalid_cert.pem"), - [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), - ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), - ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, - NewClientDerCert = public_key:pkix_sign(ClientOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), - NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, [{verify, verify_peer} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, NewClientOpts}]), - - tcp_delivery_workaround(Server, {error, "bad certificate"}, - Client, {error,"bad certificate"}). - -tcp_delivery_workaround(Server, ServerMsg, Client, ClientMsg) -> - receive - {Server, ServerMsg} -> - client_msg(Client, ClientMsg); - {Client, ClientMsg} -> - server_msg(Server, ServerMsg); - {Client, {error,closed}} -> - server_msg(Server, ServerMsg); - {Server, {error,closed}} -> - client_msg(Client, ClientMsg); - {Client, {error, esslconnect}} -> - server_msg(Server, ServerMsg); - {Server, {error, esslaccept}} -> - client_msg(Client, ClientMsg) - end. - -client_msg(Client, ClientMsg) -> - receive - {Client, ClientMsg} -> - ok; - {Client, {error,closed}} -> - test_server:format("client got close"), - ok; - {Client, {error, esslconnect}} -> - test_server:format("client got econnaborted"), - ok; - Unexpected -> - test_server:fail(Unexpected) - end. - -server_msg(Server, ServerMsg) -> - receive - {Server, ServerMsg} -> - ok; - {Server, {error,closed}} -> - test_server:format("server got close"), - ok; - {Server, {error, esslaccept}} -> - test_server:format("server got econnaborted"), - ok; - Unexpected -> - test_server:fail(Unexpected) - end. - -%%-------------------------------------------------------------------- -cert_expired(doc) -> - ["Test server with invalid signature"]; - -cert_expired(suite) -> - []; - -cert_expired(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - PrivDir = ?config(priv_dir, Config), - - KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), - [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), - Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), - - ServerCertFile = proplists:get_value(certfile, ServerOpts), - NewServerCertFile = filename:join(PrivDir, "server/expired_cert.pem"), - [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), - OTPCert = public_key:pkix_decode_cert(DerCert, otp), - OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate, - - {Year, Month, Day} = date(), - {Hours, Min, Sec} = time(), - NotBeforeStr = lists:flatten(io_lib:format("~p~s~s~s~s~sZ",[Year-2, - two_digits_str(Month), - two_digits_str(Day), - two_digits_str(Hours), - two_digits_str(Min), - two_digits_str(Sec)])), - NotAfterStr = lists:flatten(io_lib:format("~p~s~s~s~s~sZ",[Year-1, - two_digits_str(Month), - two_digits_str(Day), - two_digits_str(Hours), - two_digits_str(Min), - two_digits_str(Sec)])), - NewValidity = {'Validity', {generalTime, NotBeforeStr}, {generalTime, NotAfterStr}}, - - test_server:format("Validity: ~p ~n NewValidity: ~p ~n", - [OTPTbsCert#'OTPTBSCertificate'.validity, NewValidity]), - - NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{validity = NewValidity}, - NewServerDerCert = public_key:pkix_sign(NewOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), - NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, NewServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{verify, verify_peer} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, {error, "certificate expired"}, - Client, {error, "certificate expired"}). - -two_digits_str(N) when N < 10 -> - lists:flatten(io_lib:format("0~p", [N])); -two_digits_str(N) -> - lists:flatten(io_lib:format("~p", [N])). - -%%-------------------------------------------------------------------- - -client_with_cert_cipher_suites_handshake(doc) -> - ["Test that client with a certificate without keyEncipherment usage " - " extension can connect to a server with restricted cipher suites "]; - -client_with_cert_cipher_suites_handshake(suite) -> - []; - -client_with_cert_cipher_suites_handshake(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts_digital_signature_only, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, [{active, true}, - {ciphers, ssl_test_lib:rsa_non_signed_suites()} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, [{active, true} - | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -verify_fun_always_run_client(doc) -> - ["Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"]; -verify_fun_always_run_client(suite) -> - []; -verify_fun_always_run_client(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - %% If user verify fun is called correctly we fail the connection. - %% otherwise we can not tell this case apart form where we miss - %% to call users verify fun - FunAndState = {fun(_,{extension, _}, UserState) -> - {unknown, UserState}; - (_, valid, [ChainLen]) -> - {valid, [ChainLen + 1]}; - (_, valid_peer, [2]) -> - {fail, "verify_fun_was_always_run"}; - (_, valid_peer, UserState) -> - {valid, UserState} - end, [0]}, - - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, FunAndState} - | ClientOpts]}]), - %% Server error may be esslaccept or closed depending on timing - %% this is not a bug it is a circumstance of how tcp works! - receive - {Server, ServerError} -> - test_server:format("Server Error ~p~n", [ServerError]) - end, - - ssl_test_lib:check_result(Client, {error, esslconnect}). - -%%-------------------------------------------------------------------- -verify_fun_always_run_server(doc) -> - ["Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"]; -verify_fun_always_run_server(suite) -> - []; -verify_fun_always_run_server(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - %% If user verify fun is called correctly we fail the connection. - %% otherwise we can not tell this case apart form where we miss - %% to call users verify fun - FunAndState = {fun(_,{extension, _}, UserState) -> - {unknown, UserState}; - (_, valid, [ChainLen]) -> - {valid, [ChainLen + 1]}; - (_, valid_peer, [2]) -> - {fail, "verify_fun_was_always_run"}; - (_, valid_peer, UserState) -> - {valid, UserState} - end, [0]}, - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, FunAndState} | - ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, - [{verify, verify_peer} - | ClientOpts]}]), - - %% Client error may be esslconnect or closed depending on timing - %% this is not a bug it is a circumstance of how tcp works! - receive - {Client, ClientError} -> - test_server:format("Client Error ~p~n", [ClientError]) - end, - - ssl_test_lib:check_result(Server, {error, esslaccept}). - -%%-------------------------------------------------------------------- -unknown_server_ca_fail(doc) -> - ["Test that the client fails if the ca is unknown in verify_peer mode"]; -unknown_server_ca_fail(suite) -> - []; -unknown_server_ca_fail(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - FunAndState = {fun(_,{bad_cert, unknown_ca} = Reason, _) -> - {fail, Reason}; - (_,{extension, _}, UserState) -> - {unknown, UserState}; - (_, valid, UserState) -> - {valid, [test_to_update_user_state | UserState]}; - (_, valid_peer, UserState) -> - {valid, UserState} - end, []}, - - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, FunAndState} - | ClientOpts]}]), - - ssl_test_lib:check_result(Server, {error,"unknown ca"}, - Client, {error, "unknown ca"}). - -%%-------------------------------------------------------------------- -unknown_server_ca_accept_verify_none(doc) -> - ["Test that the client succeds if the ca is unknown in verify_none mode"]; -unknown_server_ca_accept_verify_none(suite) -> - []; -unknown_server_ca_accept_verify_none(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, - [{verify, verify_none}| ClientOpts]}]), + ClientOpts = ?config(client_opts, Config), - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). -%%-------------------------------------------------------------------- -unknown_server_ca_accept_verify_peer(doc) -> - ["Test that the client succeds if the ca is unknown in verify_peer mode" - " with a verify_fun that accepts the unknown ca error"]; -unknown_server_ca_accept_verify_peer(suite) -> - []; -unknown_server_ca_accept_verify_peer(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - FunAndState = {fun(_,{bad_cert, unknown_ca}, UserState) -> - {valid, UserState}; - (_,{bad_cert, _} = Reason, _) -> - {fail, Reason}; - (_,{extension, _}, UserState) -> - {unknown, UserState}; - (_, valid, UserState) -> - {valid, UserState}; - (_, valid_peer, UserState) -> - {valid, UserState} - end, []}, - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, FunAndState}| ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + Data = "From erlang to erlang", + N = 10, -%%-------------------------------------------------------------------- -unknown_server_ca_accept_backwardscompatibility(doc) -> - ["Test that old style verify_funs will work"]; -unknown_server_ca_accept_backwardscompatibility(suite) -> - []; -unknown_server_ca_accept_backwardscompatibility(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - AcceptBadCa = fun({bad_cert,unknown_ca}, Acc) -> Acc; - (Other, Acc) -> [Other | Acc] - end, - VerifyFun = - fun(ErrorList) -> - case lists:foldl(AcceptBadCa, [], ErrorList) of - [] -> true; - [_|_] -> false - end - end, + {mfa, {ssl_test_lib, + trigger_renegotiate, [[Data, N+2]]}}, + {options, [{renegotiate_at, N} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, VerifyFun}| ClientOpts]}]), + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{reuse_sessions, false} | ClientOpts]}]), - ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:check_result(Server, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -der_input(doc) -> - ["Test to input certs and key as der"]; - -der_input(suite) -> - []; +der_input() -> + [{doc,"Test to input certs and key as der"}]. der_input(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), @@ -3516,19 +2153,19 @@ der_input(Config) when is_list(Config) -> {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). - +%%-------------------------------------------------------------------- der_input_opts(Opts) -> Certfile = proplists:get_value(certfile, Opts), CaCertsfile = proplists:get_value(cacertfile, Opts), @@ -3545,12 +2182,9 @@ der_input_opts(Opts) -> {Cert, {Asn1Type, Key}, CaCerts, DHParams}. %%-------------------------------------------------------------------- -%% different_ca_peer_sign(doc) -> +%% different_ca_peer_sign() -> %% ["Check that a CA can have a different signature algorithm than the peer cert."]; -%% different_ca_peer_sign(suite) -> -%% []; - %% different_ca_peer_sign(Config) when is_list(Config) -> %% ClientOpts = ?config(client_mix_opts, Config), %% ServerOpts = ?config(server_mix_verify_opts, Config), @@ -3558,7 +2192,7 @@ der_input_opts(Opts) -> %% {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), %% Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, %% {from, self()}, -%% {mfa, {?MODULE, send_recv_result_active_once, []}}, +%% {mfa, {ssl_test_lib, send_recv_result_active_once, []}}, %% {options, [{active, once}, %% {verify, verify_peer} | ServerOpts]}]), %% Port = ssl_test_lib:inet_port(Server), @@ -3566,7 +2200,7 @@ der_input_opts(Opts) -> %% Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, %% {host, Hostname}, %% {from, self()}, -%% {mfa, {?MODULE, +%% {mfa, {ssl_test_lib, %% send_recv_result_active_once, %% []}}, %% {options, [{active, once}, @@ -3579,12 +2213,8 @@ der_input_opts(Opts) -> %%-------------------------------------------------------------------- -no_reuses_session_server_restart_new_cert(doc) -> - ["Check that a session is not reused if the server is restarted with a new cert."]; - -no_reuses_session_server_restart_new_cert(suite) -> - []; - +no_reuses_session_server_restart_new_cert() -> + [{doc,"Check that a session is not reused if the server is restarted with a new cert."}]. no_reuses_session_server_restart_new_cert(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -3610,10 +2240,15 @@ no_reuses_session_server_restart_new_cert(Config) when is_list(Config) -> end, %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), + Monitor = erlang:monitor(process, Server), ssl_test_lib:close(Server), ssl_test_lib:close(Client0), - + receive + {'DOWN', Monitor, _, _, _} -> + ok + end, + Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, {from, self()}, @@ -3627,7 +2262,7 @@ no_reuses_session_server_restart_new_cert(Config) when is_list(Config) -> {from, self()}, {options, ClientOpts}]), receive {Client1, SessionInfo} -> - test_server:fail(session_reused_when_server_has_new_cert); + ct:fail(session_reused_when_server_has_new_cert); {Client1, _Other} -> ok end, @@ -3635,12 +2270,9 @@ no_reuses_session_server_restart_new_cert(Config) when is_list(Config) -> ssl_test_lib:close(Client1). %%-------------------------------------------------------------------- -no_reuses_session_server_restart_new_cert_file(doc) -> - ["Check that a session is not reused if a server is restarted with a new " - "cert contained in a file with the same name as the old cert."]; - -no_reuses_session_server_restart_new_cert_file(suite) -> - []; +no_reuses_session_server_restart_new_cert_file() -> + [{doc,"Check that a session is not reused if a server is restarted with a new " + "cert contained in a file with the same name as the old cert."}]. no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -3670,7 +2302,7 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> %% Make sure session is registered and we get %% new file time stamp when calling new_config! - test_server:sleep(?SLEEP* 2), + ct:sleep(?SLEEP* 2), ssl_test_lib:close(Server), ssl_test_lib:close(Client0), @@ -3690,7 +2322,7 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> {from, self()}, {options, ClientOpts}]), receive {Client1, SessionInfo} -> - test_server:fail(session_reused_when_server_has_new_cert); + ct:fail(session_reused_when_server_has_new_cert); {Client1, _Other} -> ok end, @@ -3698,11 +2330,8 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> ssl_test_lib:close(Client1). %%-------------------------------------------------------------------- -reuseaddr(doc) -> - [""]; - -reuseaddr(suite) -> - []; +reuseaddr() -> + [{doc,"Test reuseaddr option"}]. reuseaddr(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -3720,20 +2349,19 @@ reuseaddr(Config) when is_list(Config) -> {from, self()}, {mfa, {ssl_test_lib, no_result, []}}, {options, [{active, false} | ClientOpts]}]), - test_server:sleep(?SLEEP), ssl_test_lib:close(Server), ssl_test_lib:close(Client), - + Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ServerOpts]}]), Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ClientOpts]}]), ssl_test_lib:check_result(Server1, ok, Client1, ok), @@ -3741,14 +2369,51 @@ reuseaddr(Config) when is_list(Config) -> ssl_test_lib:close(Client1). %%-------------------------------------------------------------------- +tcp_reuseaddr() -> + [{doc, "Reference test case."}]. +tcp_reuseaddr(Config) when is_list(Config) -> + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {transport, gen_tcp}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{active, false}, {reuseaddr, true}]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {transport, gen_tcp}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{active, false}]}]), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, + {from, self()}, + {transport, gen_tcp}, + {mfa, {?MODULE, tcp_send_recv_result, []}}, + {options, [{active, false}, {reuseaddr, true}]}]), + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {transport, gen_tcp}, + {mfa, {?MODULE, tcp_send_recv_result, []}}, + {options, [{active, false}]}]), + + ssl_test_lib:check_result(Server1, ok, Client1, ok), + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client1). -hibernate(doc) -> - ["Check that an SSL connection that is started with option " - "{hibernate_after, 1000} indeed hibernates after 1000ms of " - "inactivity"]; +%%-------------------------------------------------------------------- -hibernate(suite) -> - []; +hibernate() -> + [{doc,"Check that an SSL connection that is started with option " + "{hibernate_after, 1000} indeed hibernates after 1000ms of " + "inactivity"}]. hibernate(Config) -> ClientOpts = ?config(client_opts, Config), @@ -3758,14 +2423,14 @@ hibernate(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), {Client, #sslsocket{pid=Pid}} = ssl_test_lib:start_client([return_socket, {node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, [{hibernate_after, 1000}|ClientOpts]}]), {current_function, _} = process_info(Pid, current_function), @@ -3779,10 +2444,34 @@ hibernate(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -ssl_accept_timeout(doc) -> - ["Test ssl:ssl_accept timeout"]; -ssl_accept_timeout(suite) -> - []; +listen_socket() -> + [{doc,"Check error handling and inet compliance when calling API functions with listen sockets."}]. + +listen_socket(Config) -> + ServerOpts = ?config(server_opts, Config), + {ok, ListenSocket} = ssl:listen(0, ServerOpts), + + %% This can be a valid thing to do as + %% options are inherited by the accept socket + ok = ssl:controlling_process(ListenSocket, self()), + + {ok, _} = ssl:sockname(ListenSocket), + + {error, enotconn} = ssl:send(ListenSocket, <<"data">>), + {error, enotconn} = ssl:recv(ListenSocket, 0), + {error, enotconn} = ssl:connection_info(ListenSocket), + {error, enotconn} = ssl:peername(ListenSocket), + {error, enotconn} = ssl:peercert(ListenSocket), + {error, enotconn} = ssl:session_info(ListenSocket), + {error, enotconn} = ssl:renegotiate(ListenSocket), + {error, enotconn} = ssl:prf(ListenSocket, 'master_secret', <<"Label">>, client_random, 256), + {error, enotconn} = ssl:shutdown(ListenSocket, read_write), + + ok = ssl:close(ListenSocket). +%%-------------------------------------------------------------------- +ssl_accept_timeout() -> + [{doc,"Test ssl:ssl_accept timeout"}]. + ssl_accept_timeout(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -3806,10 +2495,9 @@ ssl_accept_timeout(Config) -> end. %%-------------------------------------------------------------------- -ssl_recv_timeout(doc) -> - ["Test ssl:ssl_accept timeout"]; -ssl_recv_timeout(suite) -> - []; +ssl_recv_timeout() -> + [{doc,"Test ssl:ssl_accept timeout"}]. + ssl_recv_timeout(Config) -> ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -3835,11 +2523,8 @@ ssl_recv_timeout(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- - -connect_twice(doc) -> - [""]; -connect_twice(suite) -> - []; +connect_twice() -> + [{doc,""}]. connect_twice(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -3849,7 +2534,7 @@ connect_twice(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{keepalive, true},{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), @@ -3857,7 +2542,7 @@ connect_twice(Config) when is_list(Config) -> ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{keepalive, true},{active, false} | ClientOpts]}]), Server ! listen, @@ -3867,11 +2552,11 @@ connect_twice(Config) when is_list(Config) -> {node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{keepalive, true},{active, false} | ClientOpts]}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -3882,13 +2567,9 @@ connect_twice(Config) when is_list(Config) -> ssl_test_lib:close(Client1). %%-------------------------------------------------------------------- -renegotiate_dos_mitigate_active(doc) -> - ["Mitigate DOS computational attack by not allowing client to renegotiate many times in a row", - "immediately after each other"]; - -renegotiate_dos_mitigate_active(suite) -> - []; - +renegotiate_dos_mitigate_active() -> + [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row", + "immediately after each other"}]. renegotiate_dos_mitigate_active(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -3898,7 +2579,7 @@ renegotiate_dos_mitigate_active(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, [ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), @@ -3914,13 +2595,9 @@ renegotiate_dos_mitigate_active(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -renegotiate_dos_mitigate_passive(doc) -> - ["Mitigate DOS computational attack by not allowing client to renegotiate many times in a row", - "immediately after each other"]; - -renegotiate_dos_mitigate_passive(suite) -> - []; - +renegotiate_dos_mitigate_passive() -> + [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row", + "immediately after each other"}]. renegotiate_dos_mitigate_passive(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -3930,7 +2607,7 @@ renegotiate_dos_mitigate_passive(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), @@ -3946,8 +2623,8 @@ renegotiate_dos_mitigate_passive(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -tcp_error_propagation_in_active_mode(doc) -> - ["Test that process recives {ssl_error, Socket, closed} when tcp error ocurres"]; +tcp_error_propagation_in_active_mode() -> + [{doc,"Test that process recives {ssl_error, Socket, closed} when tcp error ocurres"}]. tcp_error_propagation_in_active_mode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -3976,11 +2653,9 @@ tcp_error_propagation_in_active_mode(Config) when is_list(Config) -> ssl_test_lib:check_result(Client, {ssl_closed, SslSocket}). - %%-------------------------------------------------------------------- - -recv_error_handling(doc) -> - ["Special case of call error handling"]; +recv_error_handling() -> + [{doc,"Special case of call error handling"}]. recv_error_handling(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -4000,11 +2675,11 @@ recv_error_handling(Config) when is_list(Config) -> ssl:close(SslSocket), ssl_test_lib:check_result(Server, ok). - %%-------------------------------------------------------------------- -rizzo(doc) -> ["Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as it is - vunrable to Rizzo/Dungon attack"]; +rizzo() -> + [{doc, "Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as it is + vunrable to Rizzo/Dungon attack"}]. rizzo(Config) when is_list(Config) -> Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128], @@ -4013,8 +2688,8 @@ rizzo(Config) when is_list(Config) -> run_send_recv_rizzo(Ciphers, Config, Version, {?MODULE, send_recv_result_active_rizzo, []}). %%-------------------------------------------------------------------- -no_rizzo_rc4(doc) -> - ["Test that there is no 1/n-1-split for RC4 as it is not vunrable to Rizzo/Dungon attack"]; +no_rizzo_rc4() -> + [{doc,"Test that there is no 1/n-1-split for RC4 as it is not vunrable to Rizzo/Dungon attack"}]. no_rizzo_rc4(Config) when is_list(Config) -> Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(),Y == rc4_128], @@ -4024,58 +2699,160 @@ no_rizzo_rc4(Config) when is_list(Config) -> {?MODULE, send_recv_result_active_no_rizzo, []}). %%-------------------------------------------------------------------- -run_send_recv_rizzo(Ciphers, Config, Version, Mfa) -> - Result = lists:map(fun(Cipher) -> - rizzo_test(Cipher, Config, Version, Mfa) end, - Ciphers), - case lists:flatten(Result) of - [] -> - ok; - Error -> - test_server:format("Cipher suite errors: ~p~n", [Error]), - test_server:fail(cipher_suite_failed_see_test_case_log) - end. +new_server_wants_peer_cert() -> + [{doc, "Test that server configured to do client certification does" + " not reuse session without a client certificate."}]. +new_server_wants_peer_cert(Config) when is_list(Config) -> + ServerOpts = ?config(server_opts, Config), + VServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} + | ?config(server_verification_opts, Config)], + ClientOpts = ?config(client_verification_opts, Config), -rizzo_test(Cipher, Config, Version, Mfa) -> - {ClientOpts, ServerOpts} = client_server_opts(Cipher, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, peercert_result, []}}, + {options, [ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + Monitor = erlang:monitor(process, Server), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + receive + {'DOWN', Monitor, _, _, _} -> + ok + end, + + Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, + {from, self()}, + {mfa, {?MODULE, peercert_result, []}}, + {options, VServerOpts}]), + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [ClientOpts]}]), + + CertFile = proplists:get_value(certfile, ClientOpts), + [{'Certificate', BinCert, _}]= ssl_test_lib:pem_to_der(CertFile), + + ServerMsg = {error, no_peercert}, + Sever1Msg = {ok, BinCert}, + + ssl_test_lib:check_result(Server, ServerMsg, Server1, Sever1Msg), + + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- +session_cache_process_list() -> + [{doc,"Test reuse of sessions (short handshake)"}]. +session_cache_process_list(Config) when is_list(Config) -> + session_cache_process(list,Config). +%%-------------------------------------------------------------------- +session_cache_process_mnesia() -> + [{doc,"Test reuse of sessions (short handshake)"}]. +session_cache_process_mnesia(Config) when is_list(Config) -> + session_cache_process(mnesia,Config). + +%%-------------------------------------------------------------------- + +versions_option() -> + [{doc,"Test API versions option to connect/listen."}]. +versions_option(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + Supported = proplists:get_value(supported, ssl:versions()), + Available = proplists:get_value(available, ssl:versions()), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, Mfa}, - {options, [{active, true}, {ciphers, [Cipher]}, - {versions, [Version]} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{versions, Supported} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, Mfa}, - {options, [{active, true} | ClientOpts]}]), + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), - Result = ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - case Result of - ok -> - []; - Error -> - [{Cipher, Error}] - end. - -client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == rsa orelse KeyAlgo == dhe_rsa -> - {?config(client_opts, Config), - ?config(server_opts, Config)}; -client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss -> - {?config(client_dsa_opts, Config), - ?config(server_dsa_opts, Config)}. - + ssl_test_lib:check_result(Server, ok, Client, ok), + Server ! listen, + + ErrClient = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{versions , Available -- Supported} | ClientOpts]}]), + receive + {Server, _} -> + ok + end, + + ssl_test_lib:check_result(ErrClient, {error, {tls_alert, "protocol version"}}). %%-------------------------------------------------------------------- -%%% Internal functions +%% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- send_recv_result(Socket) -> ssl:send(Socket, "Hello world"), {ok,"Hello world"} = ssl:recv(Socket, 11), ok. +tcp_send_recv_result(Socket) -> + gen_tcp:send(Socket, "Hello world"), + {ok,"Hello world"} = gen_tcp:recv(Socket, 11), + ok. + +basic_verify_test_no_close(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + {Server, Client}. + +basic_test(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). send_recv_result_timeout_client(Socket) -> {error, timeout} = ssl:recv(Socket, 11, 500), @@ -4103,17 +2880,6 @@ recv_close(Socket) -> ok end. -send_recv_result_active(Socket) -> - ssl:send(Socket, "Hello world"), - receive - {ssl, Socket, "H"} -> - receive - {ssl, Socket, "ello world"} -> - ok - end; - {ssl, Socket, "Hello world"} -> - ok - end. send_recv_result_active_rizzo(Socket) -> ssl:send(Socket, "Hello world"), @@ -4132,26 +2898,13 @@ send_recv_result_active_no_rizzo(Socket) -> ok end. -send_recv_result_active_once(Socket) -> - ssl:send(Socket, "Hello world"), - receive - {ssl, Socket, "H"} -> - ssl:setopts(Socket, [{active, once}]), - receive - {ssl, Socket, "ello world"} -> - ok - end; - {ssl, Socket, "Hello world"} -> - ok - end. - result_ok(_Socket) -> ok. renegotiate(Socket, Data) -> - test_server:format("Renegotiating ~n", []), + ct:log("Renegotiating ~n", []), Result = ssl:renegotiate(Socket), - test_server:format("Result ~p~n", [Result]), + ct:log("Result ~p~n", [Result]), ssl:send(Socket, Data), case Result of ok -> @@ -4162,7 +2915,7 @@ renegotiate(Socket, Data) -> renegotiate_reuse_session(Socket, Data) -> %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), renegotiate(Socket, Data). renegotiate_immediately(Socket) -> @@ -4178,9 +2931,9 @@ renegotiate_immediately(Socket) -> end, ok = ssl:renegotiate(Socket), {error, renegotiation_rejected} = ssl:renegotiate(Socket), - test_server:sleep(?RENEGOTIATION_DISABLE_TIME +1), + ct:sleep(?RENEGOTIATION_DISABLE_TIME +1), ok = ssl:renegotiate(Socket), - test_server:format("Renegotiated again"), + ct:log("Renegotiated again"), ssl:send(Socket, "Hello world"), ok. @@ -4199,27 +2952,11 @@ new_config(PrivDir, ServerOpts0) -> ServerOpts = proplists:delete(keyfile, ServerOpts2), {ok, PEM} = file:read_file(NewCaCertFile), - test_server:format("CA file content: ~p~n", [public_key:pem_decode(PEM)]), + ct:log("CA file content: ~p~n", [public_key:pem_decode(PEM)]), [{cacertfile, NewCaCertFile}, {certfile, NewCertFile}, {keyfile, NewKeyFile} | ServerOpts]. -session_cache_process_list(doc) -> - ["Test reuse of sessions (short handshake)"]; - -session_cache_process_list(suite) -> - []; -session_cache_process_list(Config) when is_list(Config) -> - session_cache_process(list,Config). - -session_cache_process_mnesia(doc) -> - ["Test reuse of sessions (short handshake)"]; - -session_cache_process_mnesia(suite) -> - []; -session_cache_process_mnesia(Config) when is_list(Config) -> - session_cache_process(mnesia,Config). - session_cache_process(_Type,Config) when is_list(Config) -> reuse_session(Config). @@ -4361,9 +3098,9 @@ erlang_ssl_receive(Socket, Data) -> io:format("Received ~p~n",[Byte]), erlang_ssl_receive(Socket, tl(Data)); Other -> - test_server:fail({unexpected_message, Other}) - after ?SLEEP * 3 -> - test_server:fail({did_not_get, Data}) + ct:fail({unexpected_message, Other}) + after ?SLEEP * 3 * test_server:timetrap_scale_factor() -> + ct:fail({did_not_get, Data}) end. receive_msg(_) -> @@ -4371,3 +3108,258 @@ receive_msg(_) -> Msg -> Msg end. + +controlling_process_result(Socket, Pid, Msg) -> + ok = ssl:controlling_process(Socket, Pid), + %% Make sure other side has evaluated controlling_process + %% before message is sent + ct:sleep(?SLEEP), + ssl:send(Socket, Msg), + no_result_msg. + +receive_s_rizzo_duong_beast() -> + receive + {ssl, _, "erver hello"} -> + receive + {ssl, _, "C"} -> + receive + {ssl, _, "lient hello"} -> + ok + end + end + end. +receive_c_rizzo_duong_beast() -> + receive + {ssl, _, "lient hello"} -> + receive + {ssl, _, "S"} -> + receive + {ssl, _, "erver hello"} -> + ok + end + end + end. + +controller_dies_result(_Socket, _Pid, _Msg) -> + receive Result -> Result end. + +get_close(Pid, Where) -> + receive + {'EXIT', Pid, _Reason} -> + receive + {_, {ssl_closed, Socket}} -> + ct:log("Socket closed ~p~n",[Socket]); + Unexpected -> + ct:log("Unexpected ~p~n",[Unexpected]), + ct:fail({line, ?LINE-1}) + after 5000 -> + ct:fail({timeout, {line, ?LINE, Where}}) + end; + Unexpected -> + ct:log("Unexpected ~p~n",[Unexpected]), + ct:fail({line, ?LINE-1}) + after 5000 -> + ct:fail({timeout, {line, ?LINE, Where}}) + end. + +run_send_recv_rizzo(Ciphers, Config, Version, Mfa) -> + Result = lists:map(fun(Cipher) -> + rizzo_test(Cipher, Config, Version, Mfa) end, + Ciphers), + case lists:flatten(Result) of + [] -> + ok; + Error -> + ct:log("Cipher suite errors: ~p~n", [Error]), + ct:fail(cipher_suite_failed_see_test_case_log) + end. + +rizzo_test(Cipher, Config, Version, Mfa) -> + {ClientOpts, ServerOpts} = client_server_opts(Cipher, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, Mfa}, + {options, [{active, true}, {ciphers, [Cipher]}, + {versions, [Version]} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, Mfa}, + {options, [{active, true} | ClientOpts]}]), + + Result = ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + case Result of + ok -> + []; + Error -> + [{Cipher, Error}] + end. + +client_server_opts({KeyAlgo,_,_}, Config) + when KeyAlgo == rsa orelse + KeyAlgo == dhe_rsa orelse + KeyAlgo == ecdhe_rsa -> + {?config(client_opts, Config), + ?config(server_opts, Config)}; +client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss -> + {?config(client_dsa_opts, Config), + ?config(server_dsa_opts, Config)}; +client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == ecdh_ecdsa orelse KeyAlgo == ecdhe_ecdsa -> + {?config(client_opts, Config), + ?config(server_ecdsa_opts, Config)}; +client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == ecdh_rsa -> + {?config(client_opts, Config), + ?config(server_ecdh_rsa_opts, Config)}. + +run_suites(Ciphers, Version, Config, Type) -> + {ClientOpts, ServerOpts} = + case Type of + rsa -> + {?config(client_opts, Config), + ?config(server_opts, Config)}; + dsa -> + {?config(client_opts, Config), + ?config(server_dsa_opts, Config)}; + anonymous -> + %% No certs in opts! + {?config(client_opts, Config), + ?config(server_anon, Config)}; + psk -> + {?config(client_psk, Config), + ?config(server_psk, Config)}; + psk_with_hint -> + {?config(client_psk, Config), + ?config(server_psk_hint, Config)}; + psk_anon -> + {?config(client_psk, Config), + ?config(server_psk_anon, Config)}; + psk_anon_with_hint -> + {?config(client_psk, Config), + ?config(server_psk_anon_hint, Config)}; + srp -> + {?config(client_srp, Config), + ?config(server_srp, Config)}; + srp_anon -> + {?config(client_srp, Config), + ?config(server_srp_anon, Config)}; + srp_dsa -> + {?config(client_srp_dsa, Config), + ?config(server_srp_dsa, Config)}; + ecdsa -> + {?config(client_opts, Config), + ?config(server_ecdsa_opts, Config)}; + ecdh_rsa -> + {?config(client_opts, Config), + ?config(server_ecdh_rsa_opts, Config)} + end, + + Result = lists:map(fun(Cipher) -> + cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end, + Ciphers), + case lists:flatten(Result) of + [] -> + ok; + Error -> + ct:log("Cipher suite errors: ~p~n", [Error]), + ct:fail(cipher_suite_failed_see_test_case_log) + end. + +erlang_cipher_suite(Suite) when is_list(Suite)-> + ssl:suite_definition(ssl_cipher:openssl_suite(Suite)); +erlang_cipher_suite(Suite) -> + Suite. + +cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> + %% process_flag(trap_exit, true), + ct:log("Testing CipherSuite ~p~n", [CipherSuite]), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + ErlangCipherSuite = erlang_cipher_suite(CipherSuite), + + ConnectionInfo = {ok, {Version, ErlangCipherSuite}}, + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, + {options, + [{ciphers,[CipherSuite]} | + ClientOpts]}]), + + Result = ssl_test_lib:wait_for_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + + case Result of + ok -> + []; + Error -> + [{ErlangCipherSuite, Error}] + end. + +connection_info_result(Socket) -> + ssl:connection_info(Socket). + +connect_dist_s(S) -> + Msg = term_to_binary({erlang,term}), + ok = ssl:send(S, Msg). + +connect_dist_c(S) -> + Test = binary_to_list(term_to_binary({erlang,term})), + {ok, Test} = ssl:recv(S, 0, 10000), + ok. + + %% First two clauses handles 1/n-1 splitting countermeasure Rizzo/Duong-Beast +treashold(N, {3,0}) -> + (N div 2) + 1; +treashold(N, {3,1}) -> + (N div 2) + 1; +treashold(N, _) -> + N + 1. + +get_invalid_inet_option(Socket) -> + {error, {options, {socket_options, foo, _}}} = ssl:getopts(Socket, [foo]), + ok. + +shutdown_result(Socket, server) -> + ssl:send(Socket, "Hej"), + ssl:shutdown(Socket, write), + {ok, "Hej hopp"} = ssl:recv(Socket, 8), + ok; + +shutdown_result(Socket, client) -> + {ok, "Hej"} = ssl:recv(Socket, 3), + ssl:send(Socket, "Hej hopp"), + ssl:shutdown(Socket, write), + ok. + +shutdown_write_result(Socket, server) -> + ct:sleep(?SLEEP), + ssl:shutdown(Socket, write); +shutdown_write_result(Socket, client) -> + ssl:recv(Socket, 0). + +dummy(_Socket) -> + %% Should not happen as the ssl connection will not be established + %% due to fatal handshake failiure + exit(kill). + +shutdown_both_result(Socket, server) -> + ct:sleep(?SLEEP), + ssl:shutdown(Socket, read_write); +shutdown_both_result(Socket, client) -> + ssl:recv(Socket, 0). + +peername_result(S) -> + ssl:peername(S). diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl new file mode 100644 index 0000000000..f76c55f670 --- /dev/null +++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl @@ -0,0 +1,977 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012-2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/.2 +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(ssl_certificate_verify_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +-include("ssl_internal.hrl"). +-include("ssl_alert.hrl"). +-include("ssl_internal.hrl"). +-include("tls_record.hrl"). +-include("tls_handshake.hrl"). + +-define(LONG_TIMEOUT, 600000). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, active}, + {group, passive}, + {group, active_once}, + {group, error_handling}]. + + +groups() -> + [{active, [], tests()}, + {active_once, [], tests()}, + {passive, [], tests()}, + {error_handling, [],error_handling_tests()}]. + +tests() -> + [server_verify_peer, + server_verify_none, + server_require_peer_cert_ok, + server_require_peer_cert_fail, + verify_fun_always_run_client, + verify_fun_always_run_server, + cert_expired, + invalid_signature_client, + invalid_signature_server, + extended_key_usage_verify_peer, + extended_key_usage_verify_none]. + +error_handling_tests()-> + [client_with_cert_cipher_suites_handshake, + server_verify_no_cacerts, + unknown_server_ca_fail, + unknown_server_ca_accept_verify_none, + unknown_server_ca_accept_verify_peer, + unknown_server_ca_accept_backwardscompatibility, + no_authority_key_identifier]. + +init_per_suite(Config0) -> + Dog = ct:timetrap(?LONG_TIMEOUT *2), + catch crypto:stop(), + try crypto:start() of + ok -> + ssl:start(), + %% make rsa certs using oppenssl + Result = + (catch make_certs:all(?config(data_dir, Config0), + ?config(priv_dir, Config0))), + ct:log("Make certs ~p~n", [Result]), + + Config1 = ssl_test_lib:make_dsa_cert(Config0), + Config = ssl_test_lib:cert_options(Config1), + [{watchdog, Dog} | Config] + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). + +init_per_group(active, Config) -> + [{active, true}, {receive_function, send_recv_result_active} | Config]; +init_per_group(active_once, Config) -> + [{active, once}, {receive_function, send_recv_result_active_once} | Config]; +init_per_group(passive, Config) -> + [{active, false}, {receive_function, send_recv_result} | Config]; +init_per_group(_, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +server_verify_peer() -> + [{doc,"Test server option verify_peer"}]. +server_verify_peer(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + Active = ?config(active, Config), + ReceiveFunction = ?config(receive_function, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active}, {verify, verify_peer} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +server_verify_none() -> + [{doc,"Test server option verify_none"}]. + +server_verify_none(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + Active = ?config(active, Config), + ReceiveFunction = ?config(receive_function, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active}, {verify, verify_none} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +server_verify_client_once() -> + [{doc,"Test server option verify_client_once"}]. + +server_verify_client_once(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + Active = ?config(active, Config), + ReceiveFunction = ?config(receive_function, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active}, {verify, verify_peer}, + {verify_client_once, true} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client0, ok), + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, + ssl_test_lib:close(Client0), + Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, result_ok, []}}, + {options, [{active, Active} | ClientOpts]}]), + + ssl_test_lib:check_result(Client1, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- + +server_require_peer_cert_ok() -> + [{doc,"Test server option fail_if_no_peer_cert when peer sends cert"}]. + +server_require_peer_cert_ok(Config) when is_list(Config) -> + ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} + | ?config(server_verification_opts, Config)], + ClientOpts = ?config(client_verification_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib,send_recv_result, []}}, + {options, [{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +server_require_peer_cert_fail() -> + [{doc,"Test server option fail_if_no_peer_cert when peer doesn't send cert"}]. + +server_require_peer_cert_fail(Config) when is_list(Config) -> + ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} + | ?config(server_verification_opts, Config)], + BadClientOpts = ?config(client_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, [{active, false} | ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{active, false} | BadClientOpts]}]), + + ssl_test_lib:check_result(Server, {error, {tls_alert, "handshake failure"}}, + Client, {error, {tls_alert, "handshake failure"}}). + + +%%-------------------------------------------------------------------- +verify_fun_always_run_client() -> + [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}]. + +verify_fun_always_run_client(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + %% If user verify fun is called correctly we fail the connection. + %% otherwise we can not tell this case apart form where we miss + %% to call users verify fun + FunAndState = {fun(_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, [ChainLen]) -> + {valid, [ChainLen + 1]}; + (_, valid_peer, [2]) -> + {fail, "verify_fun_was_always_run"}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, [0]}, + + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, FunAndState} + | ClientOpts]}]), + %% Server error may be {tls_alert,"handshake failure"} or closed depending on timing + %% this is not a bug it is a circumstance of how tcp works! + receive + {Server, ServerError} -> + ct:log("Server Error ~p~n", [ServerError]) + end, + + ssl_test_lib:check_result(Client, {error, {tls_alert, "handshake failure"}}). + +%%-------------------------------------------------------------------- +verify_fun_always_run_server() -> + [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}]. +verify_fun_always_run_server(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + %% If user verify fun is called correctly we fail the connection. + %% otherwise we can not tell this case apart form where we miss + %% to call users verify fun + FunAndState = {fun(_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, [ChainLen]) -> + {valid, [ChainLen + 1]}; + (_, valid_peer, [2]) -> + {fail, "verify_fun_was_always_run"}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, [0]}, + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, FunAndState} | + ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, + [{verify, verify_peer} + | ClientOpts]}]), + + %% Client error may be {tls_alert, "handshake failure" } or closed depending on timing + %% this is not a bug it is a circumstance of how tcp works! + receive + {Client, ClientError} -> + ct:log("Client Error ~p~n", [ClientError]) + end, + + ssl_test_lib:check_result(Server, {error, {tls_alert, "handshake failure"}}). + +%%-------------------------------------------------------------------- + +client_verify_none_passive() -> + [{doc,"Test client option verify_none"}]. + +client_verify_none_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false}, + {verify, verify_none} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +cert_expired() -> + [{doc,"Test server with expired certificate"}]. + +cert_expired(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + PrivDir = ?config(priv_dir, Config), + + KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + NewServerCertFile = filename:join(PrivDir, "server/expired_cert.pem"), + [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), + OTPCert = public_key:pkix_decode_cert(DerCert, otp), + OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate, + + {Year, Month, Day} = date(), + {Hours, Min, Sec} = time(), + NotBeforeStr = lists:flatten(io_lib:format("~p~s~s~s~s~sZ",[Year-2, + two_digits_str(Month), + two_digits_str(Day), + two_digits_str(Hours), + two_digits_str(Min), + two_digits_str(Sec)])), + NotAfterStr = lists:flatten(io_lib:format("~p~s~s~s~s~sZ",[Year-1, + two_digits_str(Month), + two_digits_str(Day), + two_digits_str(Hours), + two_digits_str(Min), + two_digits_str(Sec)])), + NewValidity = {'Validity', {generalTime, NotBeforeStr}, {generalTime, NotAfterStr}}, + + ct:log("Validity: ~p ~n NewValidity: ~p ~n", + [OTPTbsCert#'OTPTBSCertificate'.validity, NewValidity]), + + NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{validity = NewValidity}, + NewServerDerCert = public_key:pkix_sign(NewOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), + NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, NewServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{verify, verify_peer} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, {error, {tls_alert, "certificate expired"}}, + Client, {error, {tls_alert, "certificate expired"}}). + +two_digits_str(N) when N < 10 -> + lists:flatten(io_lib:format("0~p", [N])); +two_digits_str(N) -> + lists:flatten(io_lib:format("~p", [N])). + +%%-------------------------------------------------------------------- + +client_verify_none_active() -> + [{doc,"Test client option verify_none"}]. + +client_verify_none_active(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, [{active, true} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, [{active, true}, + {verify, verify_none} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +client_verify_none_active_once() -> + [{doc,"Test client option verify_none"}]. + +client_verify_none_active_once(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{active, once} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active_once, + []}}, + {options, [{active, once}, + {verify, verify_none} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +extended_key_usage_verify_peer() -> + [{doc,"Test cert that has a critical extended_key_usage extension in verify_peer mode"}]. + +extended_key_usage_verify_peer(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + PrivDir = ?config(priv_dir, Config), + Active = ?config(active, Config), + ReceiveFunction = ?config(receive_function, Config), + + KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + NewServerCertFile = filename:join(PrivDir, "server/new_cert.pem"), + [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), + ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), + ServerExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']}, + ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, + ServerExtensions = ServerOTPTbsCert#'OTPTBSCertificate'.extensions, + NewServerOTPTbsCert = ServerOTPTbsCert#'OTPTBSCertificate'{extensions = + [ServerExtKeyUsageExt | + ServerExtensions]}, + NewServerDerCert = public_key:pkix_sign(NewServerOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), + NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], + + ClientCertFile = proplists:get_value(certfile, ClientOpts), + NewClientCertFile = filename:join(PrivDir, "client/new_cert.pem"), + [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), + ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), + ClientExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-clientAuth']}, + ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, + ClientExtensions = ClientOTPTbsCert#'OTPTBSCertificate'.extensions, + NewClientOTPTbsCert = ClientOTPTbsCert#'OTPTBSCertificate'{extensions = + [ClientExtKeyUsageExt | + ClientExtensions]}, + NewClientDerCert = public_key:pkix_sign(NewClientOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), + NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{verify, verify_peer}, {active, Active} | NewServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{verify, verify_peer}, {active, Active} | + NewClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +extended_key_usage_verify_none() -> + [{doc,"Test cert that has a critical extended_key_usage extension in verify_none mode"}]. + +extended_key_usage_verify_none(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + PrivDir = ?config(priv_dir, Config), + Active = ?config(active, Config), + ReceiveFunction = ?config(receive_function, Config), + + KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + NewServerCertFile = filename:join(PrivDir, "server/new_cert.pem"), + [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), + ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), + ServerExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']}, + ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, + ServerExtensions = ServerOTPTbsCert#'OTPTBSCertificate'.extensions, + NewServerOTPTbsCert = ServerOTPTbsCert#'OTPTBSCertificate'{extensions = + [ServerExtKeyUsageExt | + ServerExtensions]}, + NewServerDerCert = public_key:pkix_sign(NewServerOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), + NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], + + ClientCertFile = proplists:get_value(certfile, ClientOpts), + NewClientCertFile = filename:join(PrivDir, "client/new_cert.pem"), + [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), + ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), + ClientExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-clientAuth']}, + ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, + ClientExtensions = ClientOTPTbsCert#'OTPTBSCertificate'.extensions, + NewClientOTPTbsCert = ClientOTPTbsCert#'OTPTBSCertificate'{extensions = + [ClientExtKeyUsageExt | + ClientExtensions]}, + NewClientDerCert = public_key:pkix_sign(NewClientOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), + NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{verify, verify_none}, {active, Active} | NewServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{verify, verify_none}, {active, Active} | NewClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +no_authority_key_identifier() -> + [{doc, "Test cert that does not have authorityKeyIdentifier extension" + " but are present in trusted certs db."}]. + +no_authority_key_identifier(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_opts, Config), + PrivDir = ?config(priv_dir, Config), + + KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), + + CertFile = proplists:get_value(certfile, ServerOpts), + NewCertFile = filename:join(PrivDir, "server/new_cert.pem"), + [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(CertFile), + OTPCert = public_key:pkix_decode_cert(DerCert, otp), + OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate, + Extensions = OTPTbsCert#'OTPTBSCertificate'.extensions, + NewExtensions = delete_authority_key_extension(Extensions, []), + NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{extensions = NewExtensions}, + + ct:log("Extensions ~p~n, NewExtensions: ~p~n", [Extensions, NewExtensions]), + + NewDerCert = public_key:pkix_sign(NewOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewCertFile, [{'Certificate', NewDerCert, not_encrypted}]), + NewServerOpts = [{certfile, NewCertFile} | proplists:delete(certfile, ServerOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, NewServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{verify, verify_peer} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +delete_authority_key_extension([], Acc) -> + lists:reverse(Acc); +delete_authority_key_extension([#'Extension'{extnID = ?'id-ce-authorityKeyIdentifier'} | Rest], + Acc) -> + delete_authority_key_extension(Rest, Acc); +delete_authority_key_extension([Head | Rest], Acc) -> + delete_authority_key_extension(Rest, [Head | Acc]). + +%%-------------------------------------------------------------------- + +invalid_signature_server() -> + [{doc,"Test client with invalid signature"}]. + +invalid_signature_server(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + PrivDir = ?config(priv_dir, Config), + + KeyFile = filename:join(PrivDir, "server/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + NewServerCertFile = filename:join(PrivDir, "server/invalid_cert.pem"), + [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), + ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), + ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, + NewServerDerCert = public_key:pkix_sign(ServerOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), + NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, NewServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{verify, verify_peer} | ClientOpts]}]), + + tcp_delivery_workaround(Server, {error, {tls_alert, "bad certificate"}}, + Client, {error, {tls_alert, "bad certificate"}}). + +%%-------------------------------------------------------------------- + +invalid_signature_client() -> + [{doc,"Test server with invalid signature"}]. + +invalid_signature_client(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + PrivDir = ?config(priv_dir, Config), + + KeyFile = filename:join(PrivDir, "client/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), + + ClientCertFile = proplists:get_value(certfile, ClientOpts), + NewClientCertFile = filename:join(PrivDir, "client/invalid_cert.pem"), + [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), + ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), + ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, + NewClientDerCert = public_key:pkix_sign(ClientOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), + NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, [{verify, verify_peer} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, NewClientOpts}]), + + tcp_delivery_workaround(Server, {error, {tls_alert, "bad certificate"}}, + Client, {error, {tls_alert, "bad certificate"}}). + + +%%-------------------------------------------------------------------- + +client_with_cert_cipher_suites_handshake() -> + [{doc, "Test that client with a certificate without keyEncipherment usage " + " extension can connect to a server with restricted cipher suites "}]. +client_with_cert_cipher_suites_handshake(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts_digital_signature_only, Config), + ServerOpts = ?config(server_verification_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, [{active, true}, + {ciphers, ssl_test_lib:rsa_non_signed_suites()} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, [{active, true} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +server_verify_no_cacerts() -> + [{doc,"Test server must have cacerts if it wants to verify client"}]. +server_verify_no_cacerts(Config) when is_list(Config) -> + ServerOpts = ?config(server_opts, Config), + {_, ServerNode, _} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, [{verify, verify_peer} + | ServerOpts]}]), + + ssl_test_lib:check_result(Server, {error, {options, {cacertfile, ""}}}). + + +%%-------------------------------------------------------------------- +unknown_server_ca_fail() -> + [{doc,"Test that the client fails if the ca is unknown in verify_peer mode"}]. +unknown_server_ca_fail(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + FunAndState = {fun(_,{bad_cert, unknown_ca} = Reason, _) -> + {fail, Reason}; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, UserState) -> + {valid, [test_to_update_user_state | UserState]}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, []}, + + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, FunAndState} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, {error, {tls_alert, "unknown ca"}}, + Client, {error, {tls_alert, "unknown ca"}}). + +%%-------------------------------------------------------------------- +unknown_server_ca_accept_verify_none() -> + [{doc,"Test that the client succeds if the ca is unknown in verify_none mode"}]. +unknown_server_ca_accept_verify_none(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, + [{verify, verify_none}| ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +unknown_server_ca_accept_verify_peer() -> + [{doc, "Test that the client succeds if the ca is unknown in verify_peer mode" + " with a verify_fun that accepts the unknown ca error"}]. +unknown_server_ca_accept_verify_peer(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + FunAndState = {fun(_,{bad_cert, unknown_ca}, UserState) -> + {valid, UserState}; + (_,{bad_cert, _} = Reason, _) -> + {fail, Reason}; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, UserState) -> + {valid, UserState}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, []}, + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, FunAndState}| ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +unknown_server_ca_accept_backwardscompatibility() -> + [{doc,"Test that old style verify_funs will work"}]. +unknown_server_ca_accept_backwardscompatibility(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + AcceptBadCa = fun({bad_cert,unknown_ca}, Acc) -> Acc; + (Other, Acc) -> [Other | Acc] + end, + VerifyFun = + fun(ErrorList) -> + case lists:foldl(AcceptBadCa, [], ErrorList) of + [] -> true; + [_|_] -> false + end + end, + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, VerifyFun}| ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- + +tcp_delivery_workaround(Server, ServerMsg, Client, ClientMsg) -> + receive + {Server, ServerMsg} -> + client_msg(Client, ClientMsg); + {Client, ClientMsg} -> + server_msg(Server, ServerMsg); + {Client, {error,closed}} -> + server_msg(Server, ServerMsg); + {Server, {error,closed}} -> + client_msg(Client, ClientMsg) + end. + +client_msg(Client, ClientMsg) -> + receive + {Client, ClientMsg} -> + ok; + {Client, {error,closed}} -> + ct:log("client got close"), + ok; + {Client, {error, Reason}} -> + ct:log("client got econnaborted: ~p", [Reason]), + ok; + Unexpected -> + ct:fail(Unexpected) + end. +server_msg(Server, ServerMsg) -> + receive + {Server, ServerMsg} -> + ok; + {Server, {error,closed}} -> + ct:log("server got close"), + ok; + {Server, {error, Reason}} -> + ct:log("server got econnaborted: ~p", [Reason]), + ok; + Unexpected -> + ct:fail(Unexpected) + end. diff --git a/lib/ssl/test/ssl_cipher_SUITE.erl b/lib/ssl/test/ssl_cipher_SUITE.erl index ea1d9dc90c..45e91786d4 100644 --- a/lib/ssl/test/ssl_cipher_SUITE.erl +++ b/lib/ssl/test/ssl_cipher_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -25,22 +25,24 @@ -include_lib("common_test/include/ct.hrl"). -include("ssl_internal.hrl"). --include("ssl_record.hrl"). +-include("tls_record.hrl"). -include("ssl_cipher.hrl"). -include("ssl_alert.hrl"). -define(TIMEOUT, 600000). -%% Test server callback functions %%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initialization before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [aes_decipher_good, aes_decipher_good_tls11, aes_decipher_fail, aes_decipher_fail_tls11]. + +groups() -> + []. + init_per_suite(Config) -> try crypto:start() of ok -> @@ -48,81 +50,30 @@ init_per_suite(Config) -> catch _:_ -> {skip, "Crypto did not start"} end. -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- + end_per_suite(_Config) -> ssl:stop(), application:stop(crypto). -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initialization before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initialization before each test case -%%-------------------------------------------------------------------- -init_per_testcase(_TestCase, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = ssl_test_lib:timetrap(?TIMEOUT), - [{watchdog, Dog} | Config]. - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(_TestCase, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end. - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. - -all() -> - [aes_decipher_good, aes_decipher_good_tls11, aes_decipher_fail, aes_decipher_fail_tls11]. - -groups() -> - []. - init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. +init_per_testcase(_TestCase, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ct:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. -%% Test cases starts here. -%%-------------------------------------------------------------------- -aes_decipher_good(doc) -> - ["Decipher a known cryptotext."]; +end_per_testcase(_TestCase, Config) -> + Config. -aes_decipher_good(suite) -> - []; +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +aes_decipher_good() -> + [{doc,"Decipher a known cryptotext."}]. aes_decipher_good(Config) when is_list(Config) -> HashSz = 32, @@ -142,11 +93,8 @@ aes_decipher_good(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -aes_decipher_good_tls11(doc) -> - ["Decipher a known TLS 1.1 cryptotext."]; - -aes_decipher_good_tls11(suite) -> - []; +aes_decipher_good_tls11() -> + [{doc,"Decipher a known TLS 1.1 cryptotext."}]. %% the fragment is actuall a TLS 1.1 record, with %% Version = TLS 1.1, we get the correct NextIV in #cipher_state @@ -169,11 +117,8 @@ aes_decipher_good_tls11(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -aes_decipher_fail(doc) -> - ["Decipher a known cryptotext."]; - -aes_decipher_fail(suite) -> - []; +aes_decipher_fail() -> + [{doc,"Decipher a known cryptotext."}]. %% same as above, last byte of key replaced aes_decipher_fail(Config) when is_list(Config) -> @@ -196,11 +141,8 @@ aes_decipher_fail(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -aes_decipher_fail_tls11(doc) -> - ["Decipher a known TLS 1.1 cryptotext."]; - -aes_decipher_fail_tls11(suite) -> - []; +aes_decipher_fail_tls11() -> + [{doc,"Decipher a known TLS 1.1 cryptotext."}]. %% same as above, last byte of key replaced %% stricter padding checks in TLS 1.1 mean we get an alert instead @@ -213,9 +155,11 @@ aes_decipher_fail_tls11(Config) when is_list(Config) -> 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>, Version = {3,2}, - #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), + #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = + ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), Version1 = {3,3}, - #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), + #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = + ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), ok. %%-------------------------------------------------------------------- diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl index 818f7f1897..7bfd678f4b 100644 --- a/lib/ssl/test/ssl_dist_SUITE.erl +++ b/lib/ssl/test/ssl_dist_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -19,7 +19,7 @@ -module(ssl_dist_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -35,7 +35,10 @@ nodename} ). -%% Test server callback functions +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -54,6 +57,7 @@ end_per_group(_GroupName, Config) -> init_per_suite(Config0) -> try crypto:start() of ok -> + %% Currently no ct function avilable for is_cover! case test_server:is_cover() of false -> Config = add_ssl_opts_config(Config0), @@ -98,11 +102,13 @@ common_end(_, Config) -> Dog = ?config(watchdog, Config), ?t:timetrap_cancel(Dog), ok. + %%-------------------------------------------------------------------- -%% Test cases starts here. +%% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- -basic(doc) -> - ["Test that two nodes can connect via ssl distribution"]; + +basic() -> + [{doc,"Test that two nodes can connect via ssl distribution"}]. basic(Config) when is_list(Config) -> NH1 = start_ssl_node(Config), Node1 = NH1#node_handle.nodename, @@ -162,8 +168,8 @@ basic(Config) when is_list(Config) -> success(Config). %%-------------------------------------------------------------------- -payload(doc) -> - ["Test that send a lot of data between the ssl distributed noes"]; +payload() -> + [{doc,"Test that send a lot of data between the ssl distributed noes"}]. payload(Config) when is_list(Config) -> NH1 = start_ssl_node(Config), Node1 = NH1#node_handle.nodename, @@ -204,8 +210,8 @@ payload(Config) when is_list(Config) -> stop_ssl_node(NH2), success(Config). %%-------------------------------------------------------------------- -plain_options(doc) -> - ["Test specifying additional options"]; +plain_options() -> + [{doc,"Test specifying additional options"}]. plain_options(Config) when is_list(Config) -> DistOpts = "-ssl_dist_opt server_secure_renegotiate true " "client_secure_renegotiate true " @@ -228,8 +234,8 @@ plain_options(Config) when is_list(Config) -> stop_ssl_node(NH2), success(Config). %%-------------------------------------------------------------------- -plain_verify_options(doc) -> - ["Test specifying additional options"]; +plain_verify_options() -> + [{doc,"Test specifying additional options"}]. plain_verify_options(Config) when is_list(Config) -> DistOpts = "-ssl_dist_opt server_secure_renegotiate true " "client_secure_renegotiate true " @@ -251,7 +257,7 @@ plain_verify_options(Config) when is_list(Config) -> success(Config). %%-------------------------------------------------------------------- -%%% Internal functions +%%% Internal functions ----------------------------------------------- %%-------------------------------------------------------------------- %% ssl_node side api diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index 363a0be594..a40f07fd07 100644 --- a/lib/ssl/test/ssl_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_handshake_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -25,8 +25,11 @@ -include_lib("common_test/include/ct.hrl"). -include("ssl_internal.hrl"). --include("ssl_handshake.hrl"). +-include("tls_handshake.hrl"). +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ @@ -34,6 +37,9 @@ all() -> [ decode_single_hello_extension_correctly, decode_unknown_hello_extension_correctly]. +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- decode_hello_handshake(_Config) -> HelloPacket = <<16#02, 16#00, 16#00, 16#44, 16#03, 16#03, 16#4e, 16#7f, 16#c1, 16#03, 16#35, @@ -49,20 +55,20 @@ decode_hello_handshake(_Config) -> 16#70, 16#64, 16#79, 16#2f, 16#32>>, Version = {3, 0}, - {Records, _Buffer} = ssl_handshake:get_tls_handshake(Version, HelloPacket, <<>>), + {Records, _Buffer} = tls_handshake:get_tls_handshake(Version, HelloPacket, <<>>), {Hello, _Data} = hd(Records), #renegotiation_info{renegotiated_connection = <<0>>} = Hello#server_hello.renegotiation_info. decode_single_hello_extension_correctly(_Config) -> Renegotiation = <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(1), 0>>, - Extensions = ssl_handshake:dec_hello_extensions(Renegotiation, []), + Extensions = tls_handshake:dec_hello_extensions(Renegotiation, []), [{renegotiation_info,#renegotiation_info{renegotiated_connection = <<0>>}}] = Extensions. decode_unknown_hello_extension_correctly(_Config) -> FourByteUnknown = <<16#CA,16#FE, ?UINT16(4), 3, 0, 1, 2>>, Renegotiation = <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(1), 0>>, - Extensions = ssl_handshake:dec_hello_extensions(<<FourByteUnknown/binary, Renegotiation/binary>>, []), + Extensions = tls_handshake:dec_hello_extensions(<<FourByteUnknown/binary, Renegotiation/binary>>, []), [{renegotiation_info,#renegotiation_info{renegotiated_connection = <<0>>}}] = Extensions. diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl new file mode 100644 index 0000000000..30c0a67a36 --- /dev/null +++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl @@ -0,0 +1,363 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(ssl_npn_handshake_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). +-include_lib("common_test/include/ct.hrl"). + +-define(SLEEP, 500). +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'}]. + +groups() -> + [ + {'tlsv1.2', [], next_protocol_tests()}, + {'tlsv1.1', [], next_protocol_tests()}, + {'tlsv1', [], next_protocol_tests()}, + {'sslv3', [], next_protocol_not_supported()} + ]. + +next_protocol_tests() -> + [validate_empty_protocols_are_not_allowed, + validate_empty_advertisement_list_is_allowed, + validate_advertisement_must_be_a_binary_list, + validate_client_protocols_must_be_a_tuple, + normal_npn_handshake_server_preference, + normal_npn_handshake_client_preference, + fallback_npn_handshake, + fallback_npn_handshake_server_preference, + client_negotiate_server_does_not_support, + no_client_negotiate_but_server_supports_npn, + renegotiate_from_client_after_npn_handshake, + npn_handshake_session_reused + ]. + +next_protocol_not_supported() -> + [npn_not_supported_client, + npn_not_supported_server + ]. + +init_per_suite(Config) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl:start(), + Result = + (catch make_certs:all(?config(data_dir, Config), + ?config(priv_dir, Config))), + ct:log("Make certs ~p~n", [Result]), + ssl_test_lib:cert_options(Config) + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). + + +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName), + Config; + false -> + {skip, "Missing crypto support"} + end; + _ -> + ssl:start(), + Config + end. + +end_per_group(_GroupName, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +validate_empty_protocols_are_not_allowed(Config) when is_list(Config) -> + {error, {options, {next_protocols_advertised, {invalid_protocol, <<>>}}}} + = (catch ssl:listen(9443, + [{next_protocols_advertised, [<<"foo/1">>, <<"">>]}])), + {error, {options, {client_preferred_next_protocols, {invalid_protocol, <<>>}}}} + = (catch ssl:connect({127,0,0,1}, 9443, + [{client_preferred_next_protocols, + {client, [<<"foo/1">>, <<"">>], <<"foox/1">>}}], infinity)), + Option = {client_preferred_next_protocols, {invalid_protocol, <<"">>}}, + {error, {options, Option}} = (catch ssl:connect({127,0,0,1}, 9443, [Option], infinity)). + +%-------------------------------------------------------------------------------- + +validate_empty_advertisement_list_is_allowed(Config) when is_list(Config) -> + Option = {next_protocols_advertised, []}, + {ok, Socket} = ssl:listen(0, [Option]), + ssl:close(Socket). +%-------------------------------------------------------------------------------- + +validate_advertisement_must_be_a_binary_list(Config) when is_list(Config) -> + Option = {next_protocols_advertised, blah}, + {error, {options, Option}} = (catch ssl:listen(9443, [Option])). +%-------------------------------------------------------------------------------- + +validate_client_protocols_must_be_a_tuple(Config) when is_list(Config) -> + Option = {client_preferred_next_protocols, [<<"foo/1">>]}, + {error, {options, Option}} = (catch ssl:connect({127,0,0,1}, 9443, [Option])). + +%-------------------------------------------------------------------------------- + +normal_npn_handshake_server_preference(Config) when is_list(Config) -> + run_npn_handshake(Config, + [{client_preferred_next_protocols, + {server, [<<"http/1.0">>, <<"http/1.1">>], <<"http/1.1">>}}], + [{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}], + {ok, <<"http/1.1">>}). +%-------------------------------------------------------------------------------- + +normal_npn_handshake_client_preference(Config) when is_list(Config) -> + run_npn_handshake(Config, + [{client_preferred_next_protocols, + {client, [<<"http/1.0">>, <<"http/1.1">>], <<"http/1.1">>}}], + [{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}], + {ok, <<"http/1.0">>}). + +%-------------------------------------------------------------------------------- + +fallback_npn_handshake(Config) when is_list(Config) -> + run_npn_handshake(Config, + [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}}], + [{next_protocols_advertised, [<<"spdy/1">>, <<"http/1.1">>, <<"http/1.0">>]}], + {ok, <<"http/1.1">>}). +%-------------------------------------------------------------------------------- + +fallback_npn_handshake_server_preference(Config) when is_list(Config) -> + run_npn_handshake(Config, + [{client_preferred_next_protocols, {server, [<<"spdy/2">>], <<"http/1.1">>}}], + [{next_protocols_advertised, [<<"spdy/1">>, <<"http/1.1">>, <<"http/1.0">>]}], + {ok, <<"http/1.1">>}). + +%-------------------------------------------------------------------------------- + +no_client_negotiate_but_server_supports_npn(Config) when is_list(Config) -> + run_npn_handshake(Config, + [], + [{next_protocols_advertised, [<<"spdy/1">>, <<"http/1.1">>, <<"http/1.0">>]}], + {error, next_protocol_not_negotiated}). +%-------------------------------------------------------------------------------- + + +client_negotiate_server_does_not_support(Config) when is_list(Config) -> + run_npn_handshake(Config, + [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}}], + [], + {error, next_protocol_not_negotiated}). + +%-------------------------------------------------------------------------------- +renegotiate_from_client_after_npn_handshake(Config) when is_list(Config) -> + Data = "hello world", + + ClientOpts0 = ?config(client_opts, Config), + ClientOpts = [{client_preferred_next_protocols, + {client, [<<"http/1.0">>], <<"http/1.1">>}}] ++ ClientOpts0, + ServerOpts0 = ?config(server_opts, Config), + ServerOpts = [{next_protocols_advertised, + [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0, + ExpectedProtocol = {ok, <<"http/1.0">>}, + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, ssl_receive_and_assert_npn, [ExpectedProtocol, Data]}}, + {options, ServerOpts}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, assert_npn_and_renegotiate_and_send_data, [ExpectedProtocol, Data]}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok). + +%-------------------------------------------------------------------------------- +npn_not_supported_client(Config) when is_list(Config) -> + ClientOpts0 = ?config(client_opts, Config), + PrefProtocols = {client_preferred_next_protocols, + {client, [<<"http/1.0">>], <<"http/1.1">>}}, + ClientOpts = [PrefProtocols] ++ ClientOpts0, + {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, + {port, 8888}, {host, Hostname}, + {from, self()}, {options, ClientOpts}]), + + ssl_test_lib:check_result(Client, {error, + {options, + {not_supported_in_sslv3, PrefProtocols}}}). + +%-------------------------------------------------------------------------------- +npn_not_supported_server(Config) when is_list(Config)-> + ServerOpts0 = ?config(server_opts, Config), + AdvProtocols = {next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}, + ServerOpts = [AdvProtocols] ++ ServerOpts0, + + {error, {options, {not_supported_in_sslv3, AdvProtocols}}} = ssl:listen(0, ServerOpts). + +%-------------------------------------------------------------------------------- +npn_handshake_session_reused(Config) when is_list(Config)-> + ClientOpts0 = ?config(client_opts, Config), + ClientOpts = [{client_preferred_next_protocols, + {client, [<<"http/1.0">>], <<"http/1.1">>}}] ++ ClientOpts0, + ServerOpts0 = ?config(server_opts, Config), + ServerOpts =[{next_protocols_advertised, + [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0, + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, session_info_result, []}}, + {options, ServerOpts}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result_msg, []}}, + {options, ClientOpts}]), + + SessionInfo = + receive + {Server, Info} -> + Info + end, + + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, + + %% Make sure session is registered + ct:sleep(?SLEEP), + + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + + receive + {Client1, SessionInfo} -> + ok; + {Client1, Other} -> + ct:fail(Other) + end, + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- +run_npn_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) -> + Data = "hello world", + + ClientOpts0 = ?config(client_opts, Config), + ClientOpts = ClientExtraOpts ++ ClientOpts0, + ServerOpts0 = ?config(server_opts, Config), + ServerOpts = ServerExtraOpts ++ ServerOpts0, + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, ssl_receive_and_assert_npn, [ExpectedProtocol, Data]}}, + {options, ServerOpts}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, ssl_send_and_assert_npn, [ExpectedProtocol, Data]}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok). + + +assert_npn(Socket, Protocol) -> + ct:log("Negotiated Protocol ~p, Expecting: ~p ~n", + [ssl:negotiated_next_protocol(Socket), Protocol]), + Protocol = ssl:negotiated_next_protocol(Socket). + +assert_npn_and_renegotiate_and_send_data(Socket, Protocol, Data) -> + assert_npn(Socket, Protocol), + ct:log("Renegotiating ~n", []), + ok = ssl:renegotiate(Socket), + ssl:send(Socket, Data), + assert_npn(Socket, Protocol), + ok. + +ssl_send_and_assert_npn(Socket, Protocol, Data) -> + assert_npn(Socket, Protocol), + ssl_send(Socket, Data). + +ssl_receive_and_assert_npn(Socket, Protocol, Data) -> + assert_npn(Socket, Protocol), + ssl_receive(Socket, Data). + +ssl_send(Socket, Data) -> + ct:log("Connection info: ~p~n", + [ssl:connection_info(Socket)]), + ssl:send(Socket, Data). + +ssl_receive(Socket, Data) -> + ssl_receive(Socket, Data, []). + +ssl_receive(Socket, Data, Buffer) -> + ct:log("Connection info: ~p~n", + [ssl:connection_info(Socket)]), + receive + {ssl, Socket, MoreData} -> + ct:log("Received ~p~n",[MoreData]), + NewBuffer = Buffer ++ MoreData, + case NewBuffer of + Data -> + ssl:send(Socket, "Got it"), + ok; + _ -> + ssl_receive(Socket, Data, NewBuffer) + end; + Other -> + ct:fail({unexpected_message, Other}) + after 4000 -> + ct:fail({did_not_get, Data}) + end. + + +connection_info_result(Socket) -> + ssl:connection_info(Socket). diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl new file mode 100644 index 0000000000..ef5a02abef --- /dev/null +++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl @@ -0,0 +1,131 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% + +-module(ssl_npn_hello_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). +-include("ssl_cipher.hrl"). +-include("ssl_internal.hrl"). +-include("tls_handshake.hrl"). +-include("tls_record.hrl"). +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [encode_and_decode_npn_client_hello_test, + encode_and_decode_npn_server_hello_test, + encode_and_decode_client_hello_test, + encode_and_decode_server_hello_test, + create_server_hello_with_advertised_protocols_test, + create_server_hello_with_no_advertised_protocols_test]. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +encode_and_decode_client_hello_test(_Config) -> + HandShakeData = create_client_handshake(undefined), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + {[{DecodedHandshakeMessage, _Raw}], _} = + tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>), + NextProtocolNegotiation = DecodedHandshakeMessage#client_hello.next_protocol_negotiation, + NextProtocolNegotiation = undefined. +%%-------------------------------------------------------------------- +encode_and_decode_npn_client_hello_test(_Config) -> + HandShakeData = create_client_handshake(#next_protocol_negotiation{extension_data = <<>>}), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + {[{DecodedHandshakeMessage, _Raw}], _} = + tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>), + NextProtocolNegotiation = DecodedHandshakeMessage#client_hello.next_protocol_negotiation, + NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<>>}. +%%-------------------------------------------------------------------- +encode_and_decode_server_hello_test(_Config) -> + HandShakeData = create_server_handshake(undefined), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + {[{DecodedHandshakeMessage, _Raw}], _} = + tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>), + NextProtocolNegotiation = DecodedHandshakeMessage#server_hello.next_protocol_negotiation, + NextProtocolNegotiation = undefined. +%%-------------------------------------------------------------------- +encode_and_decode_npn_server_hello_test(_Config) -> + HandShakeData = create_server_handshake(#next_protocol_negotiation{extension_data = <<6, "spdy/2">>}), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + {[{DecodedHandshakeMessage, _Raw}], _} = + tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>), + NextProtocolNegotiation = DecodedHandshakeMessage#server_hello.next_protocol_negotiation, + ct:log("~p ~n", [NextProtocolNegotiation]), + NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<6, "spdy/2">>}. + +%%-------------------------------------------------------------------- +create_server_hello_with_no_advertised_protocols_test(_Config) -> + Hello = tls_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), false, undefined, undefined, undefined), + undefined = Hello#server_hello.next_protocol_negotiation. +%%-------------------------------------------------------------------- +create_server_hello_with_advertised_protocols_test(_Config) -> + Hello = tls_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), + false, [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>], undefined, undefined), + #next_protocol_negotiation{extension_data = <<6, "spdy/1", 8, "http/1.0", 8, "http/1.1">>} = + Hello#server_hello.next_protocol_negotiation. +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- +create_client_handshake(Npn) -> + tls_handshake:encode_handshake(#client_hello{ + client_version = {1, 2}, + random = <<1:256>>, + session_id = <<>>, + cipher_suites = [?TLS_DHE_DSS_WITH_DES_CBC_SHA], + compression_methods = "", + next_protocol_negotiation = Npn, + renegotiation_info = #renegotiation_info{} + }, vsn). + +create_server_handshake(Npn) -> + tls_handshake:encode_handshake(#server_hello{ + server_version = {1, 2}, + random = <<1:256>>, + session_id = <<>>, + cipher_suite = ?TLS_DHE_DSS_WITH_DES_CBC_SHA, + compression_method = 1, + next_protocol_negotiation = Npn, + renegotiation_info = #renegotiation_info{} + }, vsn). + +create_connection_states() -> + #connection_states{ + pending_read = #connection_state{ + security_parameters = #security_parameters{ + server_random = <<1:256>>, + compression_algorithm = 1, + cipher_suite = ?TLS_DHE_DSS_WITH_DES_CBC_SHA + } + }, + + current_read = #connection_state { + secure_renegotiation = false + } + }. diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index 8ce80cb725..36f7af784d 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -41,84 +41,10 @@ -define(MANY, 1000). -define(SOME, 50). - -%% Test server callback functions %%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initialization before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- -init_per_suite(Config) -> - catch crypto:stop(), - try crypto:start() of - ok -> - application:start(public_key), - ssl:start(), - Result = - (catch make_certs:all(?config(data_dir, Config), - ?config(priv_dir, Config))), - test_server:format("Make certs ~p~n", [Result]), - ssl_test_lib:cert_options(Config) - catch _:_ -> - {skip, "Crypto did not start"} - end. -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(_Config) -> - ssl:stop(), - application:stop(crypto). -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initialization before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initialization before each test case -%%-------------------------------------------------------------------- -init_per_testcase(_TestCase, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = ssl_test_lib:timetrap(?TIMEOUT), - [{watchdog, Dog} | Config]. - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(_TestCase, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end. - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> @@ -133,7 +59,8 @@ groups() -> [{'tlsv1.2', [], packet_tests()}, {'tlsv1.1', [], packet_tests()}, {'tlsv1', [], packet_tests()}, - {'sslv3', [], packet_tests()}]. + {'sslv3', [], packet_tests()} + ]. packet_tests() -> active_packet_tests() ++ active_once_packet_tests() ++ passive_packet_tests() ++ @@ -208,6 +135,23 @@ active_packet_tests() -> header_decode_two_bytes_one_sent_active ]. +init_per_suite(Config) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl:start(), + Result = + (catch make_certs:all(?config(data_dir, Config), + ?config(priv_dir, Config))), + ct:log("Make certs ~p~n", [Result]), + ssl_test_lib:cert_options(Config) + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). init_per_group(GroupName, Config) -> case ssl_test_lib:is_tls_version(GroupName) of @@ -228,1032 +172,262 @@ init_per_group(GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +init_per_testcase(_TestCase, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ct:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. -%% Test cases starts here. -%%-------------------------------------------------------------------- -packet_raw_passive_many_small(doc) -> - ["Test packet option {packet, raw} in passive mode."]; - -packet_raw_passive_many_small(suite) -> - []; - -packet_raw_passive_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Data = "Packet option is {packet, raw}", +end_per_testcase(_TestCase, Config) -> + Config. - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?MANY]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_raw, [Data, ?MANY]}}, - {options, - [{active, false}, - {packet, raw} | - ClientOpts]}]), +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- - ssl_test_lib:check_result(Client, ok), +packet_raw_passive_many_small() -> + [{doc,"Test packet option {packet, raw} in passive mode."}]. - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). +packet_raw_passive_many_small(Config) when is_list(Config) -> + Data = "Packet option is {packet, raw}", + packet(Config, Data, send, passive_raw, ?MANY, raw, false). %%-------------------------------------------------------------------- -packet_raw_passive_some_big(doc) -> - ["Test packet option {packet, raw} in passive mode."]; - -packet_raw_passive_some_big(suite) -> - []; - -packet_raw_passive_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_raw_passive_some_big() -> + [{doc,"Test packet option {packet, raw} in passive mode."}]. +packet_raw_passive_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?SOME]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_raw, [Data, ?SOME]}}, - {options, - [{active, false}, - {packet, raw} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send, passive_raw, ?SOME, raw, false). %%-------------------------------------------------------------------- -packet_0_passive_many_small(doc) -> - ["Test packet option {packet, 0} in passive mode."]; - -packet_0_passive_many_small(suite) -> - []; - -packet_0_passive_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_0_passive_many_small() -> + [{doc,"Test packet option {packet, 0} in passive mode."}]. +packet_0_passive_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 0}, equivalent to packet raw.", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?MANY]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_raw, [Data, ?MANY]}}, - {options, [{active, false}, - {packet, 0} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, passive_raw, ?MANY, 0, false). %%-------------------------------------------------------------------- -packet_0_passive_some_big(doc) -> - ["Test packet option {packet, 0} in passive mode."]; - -packet_0_passive_some_big(suite) -> - []; - -packet_0_passive_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_0_passive_some_big() -> + [{doc,"Test packet option {packet, 0} in passive mode."}]. +packet_0_passive_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?SOME]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_raw, [Data, ?SOME]}}, - {options, [{active, false}, - {packet, 0} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, passive_raw, ?SOME, 0, false). %%-------------------------------------------------------------------- -packet_1_passive_many_small(doc) -> - ["Test packet option {packet, 1} in passive mode."]; - -packet_1_passive_many_small(suite) -> - []; - -packet_1_passive_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_1_passive_many_small() -> + [{doc,"Test packet option {packet, 1} in passive mode."}]. +packet_1_passive_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 1}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 1}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_recv_packet, - [Data, ?MANY]}}, - {options, [{active, false}, - {packet, 1} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, passive_recv_packet, ?MANY, 1, false). %%-------------------------------------------------------------------- -packet_1_passive_some_big(doc) -> - ["Test packet option {packet, 1} in passive mode."]; - -packet_1_passive_some_big(suite) -> - []; - -packet_1_passive_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_1_passive_some_big() -> + [{doc,"Test packet option {packet, 1} in passive mode."}]. +packet_1_passive_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(255, "1")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 1}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_recv_packet, - [Data, ?SOME]}}, - {options, [{active, false}, - {packet, 1} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, passive_recv_packet, ?SOME, 1, false). %%-------------------------------------------------------------------- -packet_2_passive_many_small(doc) -> - ["Test packet option {packet, 2} in passive mode"]; - -packet_2_passive_many_small(suite) -> - []; - -packet_2_passive_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_2_passive_many_small() -> + [{doc,"Test packet option {packet, 2} in passive mode"}]. +packet_2_passive_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 2}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 2}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_recv_packet, - [Data, ?MANY]}}, - {options, [{active, false}, - {packet, 2} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, passive_recv_packet, ?MANY, 2, false). %%-------------------------------------------------------------------- -packet_2_passive_some_big(doc) -> - ["Test packet option {packet, 2} in passive mode"]; - -packet_2_passive_some_big(suite) -> - []; - -packet_2_passive_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_2_passive_some_big() -> + [{doc,"Test packet option {packet, 2} in passive mode"}]. +packet_2_passive_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 2}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_recv_packet, - [Data, ?SOME]}}, - {options, [{active, false}, - {packet, 2} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, passive_recv_packet, ?SOME, 2, false). %%-------------------------------------------------------------------- -packet_4_passive_many_small(doc) -> - ["Test packet option {packet, 4} in passive mode"]; - -packet_4_passive_many_small(suite) -> - []; - -packet_4_passive_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_4_passive_many_small() -> + [{doc,"Test packet option {packet, 4} in passive mode"}]. +packet_4_passive_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 4}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, - {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 4}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_recv_packet, - [Data, ?MANY]}}, - {options, [{active, false}, - {packet, 4} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, passive_recv_packet, ?MANY, 4, false). %%-------------------------------------------------------------------- -packet_4_passive_some_big(doc) -> - ["Test packet option {packet, 4} in passive mode"]; - -packet_4_passive_some_big(suite) -> - []; - -packet_4_passive_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_4_passive_some_big() -> + [{doc,"Test packet option {packet, 4} in passive mode"}]. +packet_4_passive_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 4}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_recv_packet, - [Data, ?SOME]}}, - {options, [{active, false}, - {packet, 4} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send, passive_recv_packet, ?SOME, 4, false). %%-------------------------------------------------------------------- -packet_raw_active_once_many_small(doc) -> - ["Test packet option {packet, raw} in active once mode."]; - -packet_raw_active_once_many_small(suite) -> - []; - -packet_raw_active_once_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_raw_active_once_many_small() -> + [{doc,"Test packet option {packet, raw} in active once mode."}]. +packet_raw_active_once_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, raw}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?MANY]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, active_once_raw, - [Data, ?MANY]}}, - {options, [{active, once}, - {packet, raw} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_once_raw, ?MANY, raw, once). %%-------------------------------------------------------------------- -packet_raw_active_once_some_big(doc) -> - ["Test packet option {packet, raw} in active once mode."]; - -packet_raw_active_once_some_big(suite) -> - []; - -packet_raw_active_once_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_raw_active_once_some_big() -> + [{doc,"Test packet option {packet, raw} in active once mode."}]. +packet_raw_active_once_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?SOME]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, active_once_raw, - [Data, ?SOME]}}, - {options, [{active, once}, - {packet, raw} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_once_raw, ?SOME, raw, once). %%-------------------------------------------------------------------- -packet_0_active_once_many_small(doc) -> - ["Test packet option {packet, 0} in active once mode."]; - -packet_0_active_once_many_small(suite) -> - []; - -packet_0_active_once_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_0_active_once_many_small() -> + [{doc,"Test packet option {packet, 0} in active once mode."}]. +packet_0_active_once_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 0}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?MANY]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, active_once_raw, - [Data, ?MANY]}}, - {options, [{active, once}, - {packet, 0} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send_raw, active_once_raw, ?MANY, 0, once). %%-------------------------------------------------------------------- -packet_0_active_once_some_big(doc) -> - ["Test packet option {packet, 0} in active once mode."]; - -packet_0_active_once_some_big(suite) -> - []; - -packet_0_active_once_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_0_active_once_some_big() -> + [{doc,"Test packet option {packet, 0} in active once mode."}]. +packet_0_active_once_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw , - [Data, ?SOME]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, active_once_raw, - [Data, ?SOME]}}, - {options, [{active, once}, - {packet, 0} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_once_raw, ?SOME, 0, once). %%-------------------------------------------------------------------- -packet_1_active_once_many_small(doc) -> - ["Test packet option {packet, 1} in active once mode."]; - -packet_1_active_once_many_small(suite) -> - []; - -packet_1_active_once_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_1_active_once_many_small() -> + [{doc,"Test packet option {packet, 1} in active once mode."}]. +packet_1_active_once_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 1}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 1}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_once_packet, - [Data, ?MANY]}}, - {options, [{active, once}, - {packet, 1} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, active_once_packet, ?MANY, 1, once). %%-------------------------------------------------------------------- -packet_1_active_once_some_big(doc) -> - ["Test packet option {packet, 1} in active once mode."]; - -packet_1_active_once_some_big(suite) -> - []; - -packet_1_active_once_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_1_active_once_some_big() -> + [{doc,"Test packet option {packet, 1} in active once mode."}]. +packet_1_active_once_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(255, "1")), + packet(Config, Data, send, active_once_packet, ?SOME, 1, once). - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 1}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_once_packet, - [Data, ?SOME]}}, - {options, [{active, once}, - {packet, 1} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_2_active_once_many_small(doc) -> - ["Test packet option {packet, 2} in active once mode"]; - -packet_2_active_once_many_small(suite) -> - []; - -packet_2_active_once_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_2_active_once_many_small() -> + [{doc,"Test packet option {packet, 2} in active once mode"}]. +packet_2_active_once_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 2}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 2}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_once_packet, - [Data, ?MANY]}}, - {options, [{active, once}, - {packet, 2} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send, active_once_packet, ?MANY, 2, once). %%-------------------------------------------------------------------- -packet_2_active_once_some_big(doc) -> - ["Test packet option {packet, 2} in active once mode"]; - -packet_2_active_once_some_big(suite) -> - []; - -packet_2_active_once_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_2_active_once_some_big() -> + [{doc,"Test packet option {packet, 2} in active once mode"}]. +packet_2_active_once_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 2}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_once_packet, - [Data, ?SOME]}}, - {options, [{active, once}, - {packet, 2} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, active_once_raw, ?SOME, 2, once). %%-------------------------------------------------------------------- -packet_4_active_once_many_small(doc) -> - ["Test packet option {packet, 4} in active once mode"]; - -packet_4_active_once_many_small(suite) -> - []; - -packet_4_active_once_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_4_active_once_many_small() -> + [{doc,"Test packet option {packet, 4} in active once mode"}]. +packet_4_active_once_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 4}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 4}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_once_packet, - [Data, ?MANY]}}, - {options, [{active, once}, - {packet, 4} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, active_once_packet, ?MANY, 4, once). %%-------------------------------------------------------------------- -packet_4_active_once_some_big(doc) -> - ["Test packet option {packet, 4} in active once mode"]; - -packet_4_active_once_some_big(suite) -> - []; - -packet_4_active_once_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_4_active_once_some_big() -> + [{doc,"Test packet option {packet, 4} in active once mode"}]. +packet_4_active_once_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 4}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_once_packet, - [Data, ?SOME]}}, - {options, [{active, once}, - {packet, 4} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, active_once_packet, ?SOME, 4, once). %%-------------------------------------------------------------------- -packet_raw_active_many_small(doc) -> - ["Test packet option {packet, raw} in active mode."]; - -packet_raw_active_many_small(suite) -> - []; +packet_raw_active_many_small() -> + [{doc,"Test packet option {packet, raw} in active mode."}]. packet_raw_active_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Data = "Packet option is {packet, raw}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?MANY]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, active_raw, - [Data, ?MANY]}}, - {options, [{active, true}, - {packet, raw} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send_raw, active_raw, ?MANY, raw, true). %%-------------------------------------------------------------------- -packet_raw_active_some_big(doc) -> - ["Test packet option {packet, raw} in active mode."]; - -packet_raw_active_some_big(suite) -> - []; +packet_raw_active_some_big() -> + [{doc,"Test packet option {packet, raw} in active mode."}]. packet_raw_active_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?SOME]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, active_raw, [Data, ?SOME]}}, - {options, [{active, true}, - {packet, raw} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_raw, ?SOME, raw, true). %%-------------------------------------------------------------------- -packet_0_active_many_small(doc) -> - ["Test packet option {packet, 0} in active mode."]; - -packet_0_active_many_small(suite) -> - []; - -packet_0_active_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_0_active_many_small() -> + [{doc,"Test packet option {packet, 0} in active mode."}]. +packet_0_active_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 0}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?MANY]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, active_raw, - [Data, ?MANY]}}, - {options, [{active, true}, - {packet, 0} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_raw, ?MANY, 0, true). %%-------------------------------------------------------------------- -packet_0_active_some_big(doc) -> - ["Test packet option {packet, 0} in active mode."]; - -packet_0_active_some_big(suite) -> - []; - -packet_0_active_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_0_active_some_big() -> + [{doc,"Test packet option {packet, 0} in active mode."}]. +packet_0_active_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?SOME]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, active_raw, - [Data, ?SOME]}}, - {options, [{active, true}, - {packet, 0} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send, active_raw, ?SOME, 0, true). %%-------------------------------------------------------------------- -packet_1_active_many_small(doc) -> - ["Test packet option {packet, 1} in active mode."]; - -packet_1_active_many_small(suite) -> - []; - -packet_1_active_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_1_active_many_small() -> + [{doc,"Test packet option {packet, 1} in active mode."}]. +packet_1_active_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 1}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 1}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_packet, [Data, ?MANY]}}, - {options, [{active, true}, - {packet, 1} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, active_packet, ?MANY, 1, true). %%-------------------------------------------------------------------- -packet_1_active_some_big(doc) -> - ["Test packet option {packet, 1} in active mode."]; - -packet_1_active_some_big(suite) -> - []; - -packet_1_active_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_1_active_some_big() -> + [{doc,"Test packet option {packet, 1} in active mode."}]. +packet_1_active_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(255, "1")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 1}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_packet, [Data, ?SOME]}}, - {options, [{active, true}, - {packet, 1} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, active_packet, ?SOME, 1, true). %%-------------------------------------------------------------------- -packet_2_active_many_small(doc) -> - ["Test packet option {packet, 2} in active mode"]; - -packet_2_active_many_small(suite) -> - []; - -packet_2_active_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_2_active_many_small() -> + [{doc,"Test packet option {packet, 2} in active mode"}]. +packet_2_active_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 2}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 2}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_packet, [Data, ?MANY]}}, - {options, [{active, true}, - {packet, 2} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, active_packet, ?MANY, 2, true). %%-------------------------------------------------------------------- -packet_2_active_some_big(doc) -> - ["Test packet option {packet, 2} in active mode"]; - -packet_2_active_some_big(suite) -> - []; - -packet_2_active_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_2_active_some_big() -> + [{doc,"Test packet option {packet, 2} in active mode"}]. +packet_2_active_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 2}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_packet, [Data, ?SOME]}}, - {options, [{active, true}, - {packet, 2} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, active_packet, ?SOME, 2, true). %%-------------------------------------------------------------------- -packet_4_active_many_small(doc) -> - ["Test packet option {packet, 4} in active mode"]; - -packet_4_active_many_small(suite) -> - []; - -packet_4_active_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_4_active_many_small() -> + [{doc,"Test packet option {packet, 4} in active mode"}]. +packet_4_active_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 4}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 4}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_packet, [Data, ?MANY]}}, - {options, [{active, true}, - {packet, 4} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send, active_packet, ?MANY, 4, true). %%-------------------------------------------------------------------- -packet_4_active_some_big(doc) -> - ["Test packet option {packet, 4} in active mode"]; - -packet_4_active_some_big(suite) -> - []; - -packet_4_active_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_4_active_some_big() -> + [{doc,"Test packet option {packet, 4} in active mode"}]. +packet_4_active_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 4} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_packet, [Data, ?SOME]}}, - {options, [{active, true}, - {packet, 4} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send, active_packet, ?SOME, 4, true). %%-------------------------------------------------------------------- -packet_send_to_large(doc) -> - ["Test setting the packet option {packet, 2} on the send side"]; - -packet_send_to_large(suite) -> []; +packet_send_to_large() -> + [{doc,"Test setting the packet option {packet, 2} on the send side"}]. packet_send_to_large(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1279,16 +453,9 @@ packet_send_to_large(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). - - - - %%-------------------------------------------------------------------- -packet_wait_active(doc) -> - ["Test waiting when complete packages have not arrived"]; - -packet_wait_active(suite) -> - []; +packet_wait_active() -> + [{doc,"Test waiting when complete packages have not arrived"}]. packet_wait_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1320,11 +487,8 @@ packet_wait_active(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -packet_wait_passive(doc) -> - ["Test waiting when complete packages have not arrived"]; - -packet_wait_passive(suite) -> - []; +packet_wait_passive() -> + [{doc,"Test waiting when complete packages have not arrived"}]. packet_wait_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1353,10 +517,8 @@ packet_wait_passive(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_baddata_active(doc) -> - ["Test that if a bad packet arrives error msg is sent and socket is closed"]; -packet_baddata_active(suite) -> - []; +packet_baddata_active() -> + [{doc,"Test that if a bad packet arrives error msg is sent and socket is closed"}]. packet_baddata_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1381,18 +543,15 @@ packet_baddata_active(Config) when is_list(Config) -> {Client, {other, {ssl_error, _Socket, {invalid_packet, _}},{error,closed},1}} -> ok; Unexpected -> - test_server:fail({unexpected, Unexpected}) + ct:fail({unexpected, Unexpected}) end, ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_baddata_passive(doc) -> - ["Test that if a bad packet arrives error msg is sent and socket is closed"]; - -packet_baddata_passive(suite) -> - []; +packet_baddata_passive() -> + [{doc,"Test that if a bad packet arrives error msg is sent and socket is closed"}]. packet_baddata_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1418,19 +577,16 @@ packet_baddata_passive(Config) when is_list(Config) -> receive {Client, {other, {error, {invalid_packet, _}},{error,closed}, 1}} -> ok; Unexpected -> - test_server:fail({unexpected, Unexpected}) + ct:fail({unexpected, Unexpected}) end, ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_size_active(doc) -> - ["Test that if a packet of size larger than - packet_size arrives error msg is sent and socket is closed"]; - -packet_size_active(suite) -> - []; +packet_size_active() -> + [{doc,"Test that if a packet of size larger than + packet_size arrives error msg is sent and socket is closed"}]. packet_size_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1455,17 +611,16 @@ packet_size_active(Config) when is_list(Config) -> {Client, {other, {ssl_error, _Socket, {invalid_packet, _}},{error,closed},1}} -> ok; Unexpected -> - test_server:fail({unexpected, Unexpected}) + ct:fail({unexpected, Unexpected}) end, ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_size_passive(doc) -> - ["Test that if a packet of size larger - than packet_size arrives error msg is sent and socket is closed"]; -packet_size_passive(suite) -> []; +packet_size_passive() -> + [{doc, "Test that if a packet of size larger + than packet_size arrives error msg is sent and socket is closed"}]. packet_size_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1490,17 +645,15 @@ packet_size_passive(Config) when is_list(Config) -> receive {Client, {other, {error, {invalid_packet, _}},{error,closed},1}} -> ok; Unexpected -> - test_server:fail({unexpected, Unexpected}) + ct:fail({unexpected, Unexpected}) end, ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_cdr_decode(doc) -> - ["Test setting the packet option {packet, cdr}, {mode, binary}"]; -packet_cdr_decode(suite) -> - []; +packet_cdr_decode() -> + [{doc,"Test setting the packet option {packet, cdr}, {mode, binary}"}]. packet_cdr_decode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1532,10 +685,8 @@ packet_cdr_decode(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_cdr_decode_list(doc) -> - ["Test setting the packet option {packet, cdr} {mode, list}"]; -packet_cdr_decode_list(suite) -> - []; +packet_cdr_decode_list() -> + [{doc,"Test setting the packet option {packet, cdr} {mode, list}"}]. packet_cdr_decode_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1567,11 +718,9 @@ packet_cdr_decode_list(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_http_decode(doc) -> - ["Test setting the packet option {packet, http} {mode, binary} " - "(Body will be binary http strings are lists)"]; -packet_http_decode(suite) -> - []; +packet_http_decode() -> + [{doc, "Test setting the packet option {packet, http} {mode, binary} " + "(Body will be binary http strings are lists)"}]. packet_http_decode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1652,11 +801,9 @@ client_http_decode(Socket, HttpRequest) -> ok. %%-------------------------------------------------------------------- -packet_http_decode_list(doc) -> - ["Test setting the packet option {packet, http}, {mode, list}" - "(Body will be list too)"]; -packet_http_decode_list(suite) -> - []; +packet_http_decode_list() -> + [{doc, "Test setting the packet option {packet, http}, {mode, list}" + "(Body will be list too)"}]. packet_http_decode_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1712,11 +859,8 @@ client_http_decode_list(Socket, HttpRequest) -> ok. %%-------------------------------------------------------------------- -packet_http_bin_decode_multi(doc) -> - ["Test setting the packet option {packet, http_bin} with multiple requests"]; -packet_http_bin_decode_multi(suite) -> - []; - +packet_http_bin_decode_multi() -> + [{doc,"Test setting the packet option {packet, http_bin} with multiple requests"}]. packet_http_bin_decode_multi(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1803,11 +947,10 @@ client_http_bin_decode(_, _, _) -> ok. %%-------------------------------------------------------------------- -packet_http_error_passive(doc) -> - ["Test setting the packet option {packet, http}, {active, false}" - " with a incorrect http header." ]; -packet_http_error_passive(suite) -> - []; +packet_http_error_passive() -> + [{doc,"Test setting the packet option {packet, http}, {active, false}" + " with a incorrect http header."}]. + packet_http_error_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1865,10 +1008,9 @@ server_http_decode_error(Socket, HttpResponse) -> ok = ssl:send(Socket, HttpResponse), ok. %%-------------------------------------------------------------------- -packet_httph_active(doc) -> - ["Test setting the packet option {packet, httph}"]; -packet_httph_active(suite) -> - []; +packet_httph_active() -> + [{doc,"Test setting the packet option {packet, httph}"}]. + packet_httph_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1922,10 +1064,8 @@ client_http_decode_trailer_active(Socket) -> ok. %%-------------------------------------------------------------------- -packet_httph_bin_active(doc) -> - ["Test setting the packet option {packet, httph_bin}"]; -packet_httph_bin_active(suite) -> - []; +packet_httph_bin_active() -> + [{doc,"Test setting the packet option {packet, httph_bin}"}]. packet_httph_bin_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1973,10 +1113,9 @@ client_http_decode_trailer_bin_active(Socket) -> end, ok. %%-------------------------------------------------------------------- -packet_httph_active_once(doc) -> - ["Test setting the packet option {packet, httph}"]; -packet_httph_active_once(suite) -> - []; +packet_httph_active_once() -> + [{doc,"Test setting the packet option {packet, httph}"}]. + packet_httph_active_once(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2027,10 +1166,9 @@ client_http_decode_trailer_active_once(Socket) -> end, ok. %%-------------------------------------------------------------------- -packet_httph_bin_active_once(doc) -> - ["Test setting the packet option {packet, httph_bin}"]; -packet_httph_bin_active_once(suite) -> - []; +packet_httph_bin_active_once() -> + [{doc,"Test setting the packet option {packet, httph_bin}"}]. + packet_httph_bin_active_once(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2082,10 +1220,9 @@ client_http_decode_trailer_bin_active_once(Socket) -> %%-------------------------------------------------------------------- -packet_httph_passive(doc) -> - ["Test setting the packet option {packet, httph}"]; -packet_httph_passive(suite) -> - []; +packet_httph_passive() -> + [{doc,"Test setting the packet option {packet, httph}"}]. + packet_httph_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2123,10 +1260,9 @@ client_http_decode_trailer_passive(Socket) -> ok. %%-------------------------------------------------------------------- -packet_httph_bin_passive(doc) -> - ["Test setting the packet option {packet, httph_bin}"]; -packet_httph_bin_passive(suite) -> - []; +packet_httph_bin_passive() -> + [{doc,"Test setting the packet option {packet, httph_bin}"}]. + packet_httph_bin_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2164,10 +1300,9 @@ client_http_decode_trailer_bin_passive(Socket) -> ok. %%-------------------------------------------------------------------- -packet_line_decode(doc) -> - ["Test setting the packet option {packet, line}, {mode, binary}"]; -packet_line_decode(suite) -> - []; +packet_line_decode() -> + [{doc,"Test setting the packet option {packet, line}, {mode, binary}"}]. + packet_line_decode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2201,10 +1336,9 @@ packet_line_decode(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -packet_line_decode_list(doc) -> - ["Test setting the packet option {packet, line}, {mode, list}"]; -packet_line_decode_list(suite) -> - []; +packet_line_decode_list() -> + [{doc,"Test setting the packet option {packet, line}, {mode, list}"}]. + packet_line_decode_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2240,10 +1374,9 @@ packet_line_decode_list(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -packet_asn1_decode(doc) -> - ["Test setting the packet option {packet, asn1}"]; -packet_asn1_decode(suite) -> - []; +packet_asn1_decode() -> + [{doc,"Test setting the packet option {packet, asn1}"}]. + packet_asn1_decode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2276,10 +1409,9 @@ packet_asn1_decode(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_asn1_decode_list(doc) -> - ["Test setting the packet option {packet, asn1}"]; -packet_asn1_decode_list(suite) -> - []; +packet_asn1_decode_list() -> + [{doc,"Test setting the packet option {packet, asn1}"}]. + packet_asn1_decode_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2314,10 +1446,9 @@ packet_asn1_decode_list(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_tpkt_decode(doc) -> - ["Test setting the packet option {packet, tpkt}"]; -packet_tpkt_decode(suite) -> - []; +packet_tpkt_decode() -> + [{doc,"Test setting the packet option {packet, tpkt}"}]. + packet_tpkt_decode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2347,10 +1478,9 @@ packet_tpkt_decode(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_tpkt_decode_list(doc) -> - ["Test setting the packet option {packet, tpkt}"]; -packet_tpkt_decode_list(suite) -> - []; +packet_tpkt_decode_list() -> + [{doc,"Test setting the packet option {packet, tpkt}"}]. + packet_tpkt_decode_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2381,10 +1511,9 @@ packet_tpkt_decode_list(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -%% packet_fcgi_decode(doc) -> -%% ["Test setting the packet option {packet, fcgi}"]; -%% packet_fcgi_decode(suite) -> -%% []; +%% packet_fcgi_decode() -> +%% [{doc,"Test setting the packet option {packet, fcgi}"}]. + %% packet_fcgi_decode(Config) when is_list(Config) -> %% ClientOpts = ?config(client_opts, Config), %% ServerOpts = ?config(server_opts, Config), @@ -2416,10 +1545,8 @@ packet_tpkt_decode_list(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -packet_sunrm_decode(doc) -> - ["Test setting the packet option {packet, sunrm}"]; -packet_sunrm_decode(suite) -> - []; +packet_sunrm_decode() -> + [{doc,"Test setting the packet option {packet, sunrm}"}]. packet_sunrm_decode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2449,10 +1576,9 @@ packet_sunrm_decode(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_sunrm_decode_list(doc) -> - ["Test setting the packet option {packet, sunrm}"]; -packet_sunrm_decode_list(suite) -> - []; +packet_sunrm_decode_list() -> + [{doc,"Test setting the packet option {packet, sunrm}"}]. + packet_sunrm_decode_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2482,10 +1608,9 @@ packet_sunrm_decode_list(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -header_decode_one_byte_active(doc) -> - ["Test setting the packet option {header, 1}"]; -header_decode_one_byte_active(suite) -> - []; +header_decode_one_byte_active() -> + [{doc,"Test setting the packet option {header, 1}"}]. + header_decode_one_byte_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2516,10 +1641,9 @@ header_decode_one_byte_active(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_active(doc) -> - ["Test setting the packet option {header, 2}"]; -header_decode_two_bytes_active(suite) -> - []; +header_decode_two_bytes_active() -> + [{doc,"Test setting the packet option {header, 2}"}]. + header_decode_two_bytes_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2551,10 +1675,9 @@ header_decode_two_bytes_active(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_two_sent_active(doc) -> - ["Test setting the packet option {header, 2} and sending two byte"]; -header_decode_two_bytes_two_sent_active(suite) -> - []; +header_decode_two_bytes_two_sent_active() -> + [{doc,"Test setting the packet option {header, 2} and sending two byte"}]. + header_decode_two_bytes_two_sent_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2586,10 +1709,9 @@ header_decode_two_bytes_two_sent_active(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_one_sent_active(doc) -> - ["Test setting the packet option {header, 2} and sending one byte"]; -header_decode_two_bytes_one_sent_active(suite) -> - []; +header_decode_two_bytes_one_sent_active() -> + [{doc,"Test setting the packet option {header, 2} and sending one byte"}]. + header_decode_two_bytes_one_sent_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2620,10 +1742,9 @@ header_decode_two_bytes_one_sent_active(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_one_byte_passive(doc) -> - ["Test setting the packet option {header, 1}"]; -header_decode_one_byte_passive(suite) -> - []; +header_decode_one_byte_passive() -> + [{doc,"Test setting the packet option {header, 1}"}]. + header_decode_one_byte_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2654,10 +1775,9 @@ header_decode_one_byte_passive(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_passive(doc) -> - ["Test setting the packet option {header, 2}"]; -header_decode_two_bytes_passive(suite) -> - []; +header_decode_two_bytes_passive() -> + [{doc,"Test setting the packet option {header, 2}"}]. + header_decode_two_bytes_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2689,10 +1809,9 @@ header_decode_two_bytes_passive(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_two_sent_passive(doc) -> - ["Test setting the packet option {header, 2} and sending two byte"]; -header_decode_two_bytes_two_sent_passive(suite) -> - []; +header_decode_two_bytes_two_sent_passive() -> + [{doc,"Test setting the packet option {header, 2} and sending two byte"}]. + header_decode_two_bytes_two_sent_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2724,10 +1843,9 @@ header_decode_two_bytes_two_sent_passive(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_one_sent_passive(doc) -> - ["Test setting the packet option {header, 2} and sending one byte"]; -header_decode_two_bytes_one_sent_passive(suite) -> - []; +header_decode_two_bytes_one_sent_passive() -> + [{doc,"Test setting the packet option {header, 2} and sending one byte"}]. + header_decode_two_bytes_one_sent_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2757,7 +1875,30 @@ header_decode_two_bytes_one_sent_passive(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -%% Internal functions +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- +packet(Config, Data, Send, Recv, Quantity, Packet, Active) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, Send ,[Data, Quantity]}}, + {options, [{packet, Packet} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, Recv, [Data, Quantity]}}, + {options, [{active, Active}, + {packet, Packet} | + ClientOpts]}]), + + ssl_test_lib:check_result(Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). send_raw(Socket,_, 0) -> ssl:send(Socket, <<>>), @@ -2928,7 +2069,7 @@ client_packet_decode(Socket, [Head | Tail] = Packet) -> client_packet_decode(Socket, [Head], Tail, Packet). client_packet_decode(Socket, P1, P2, Packet) -> - test_server:format("Packet: ~p ~n", [Packet]), + ct:log("Packet: ~p ~n", [Packet]), ok = ssl:send(Socket, P1), ok = ssl:send(Socket, P2), receive diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index c97f97e70b..f95eae51b7 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -26,79 +26,8 @@ -define(TIMEOUT, 600000). -%% Test server callback functions %%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initialization before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - catch crypto:stop(), - try crypto:start() of - ok -> - application:start(public_key), - ssl:start(), - make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config)), - ssl_test_lib:cert_options(Config) - catch _:_ -> - {skip, "Crypto did not start"} - end. -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(_Config) -> - ssl:stop(), - application:stop(crypto). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initialization before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initialization before each test case -%%-------------------------------------------------------------------- -init_per_testcase(_TestCase, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = ssl_test_lib:timetrap(?TIMEOUT), - [{watchdog, Dog} | Config]. - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(_TestCase, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end. - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -138,6 +67,20 @@ payload_tests() -> client_echos_active_once_huge, client_echos_active_huge]. +init_per_suite(Config) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl:start(), + make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config)), + ssl_test_lib:cert_options(Config) + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). init_per_group(GroupName, Config) -> case ssl_test_lib:is_tls_version(GroupName) of @@ -157,15 +100,20 @@ init_per_group(GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +init_per_testcase(_TestCase, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ct:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. -%% Test cases starts here. +end_per_testcase(_TestCase, Config) -> + Config. +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- -server_echos_passive_small(doc) -> - ["Client sends 1000 bytes in passive mode to server, that receives them, " - "sends them back, and closes."]; -server_echos_passive_small(suite) -> - []; +server_echos_passive_small() -> + [{doc, "Client sends 1000 bytes in passive mode to server, that receives them, " + "sends them back, and closes."}]. server_echos_passive_small(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -179,12 +127,9 @@ server_echos_passive_small(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -server_echos_active_once_small(doc) -> - ["Client sends 1000 bytes in active once mode to server, that receives " - " them, sends them back, and closes."]; - -server_echos_active_once_small(suite) -> - []; +server_echos_active_once_small() -> + [{doc, "Client sends 1000 bytes in active once mode to server, that receives " + " them, sends them back, and closes."}]. server_echos_active_once_small(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -198,12 +143,9 @@ server_echos_active_once_small(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -server_echos_active_small(doc) -> - ["Client sends 1000 bytes in active mode to server, that receives them, " - "sends them back, and closes."]; - -server_echos_active_small(suite) -> - []; +server_echos_active_small() -> + [{doc, "Client sends 1000 bytes in active mode to server, that receives them, " + "sends them back, and closes."}]. server_echos_active_small(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -216,12 +158,9 @@ server_echos_active_small(Config) when is_list(Config) -> ClientNode, ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_passive_small(doc) -> - ["Server sends 1000 bytes in passive mode to client, that receives them, " - "sends them back, and closes."]; - -client_echos_passive_small(suite) -> - []; +client_echos_passive_small() -> + [{doc, "Server sends 1000 bytes in passive mode to client, that receives them, " + "sends them back, and closes."}]. client_echos_passive_small(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -234,12 +173,9 @@ client_echos_passive_small(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_active_once_small(doc) -> +client_echos_active_once_small() -> ["Server sends 1000 bytes in active once mode to client, that receives " - "them, sends them back, and closes."]; - -client_echos_active_once_small(suite) -> - []; + "them, sends them back, and closes."]. client_echos_active_once_small(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -252,15 +188,12 @@ client_echos_active_once_small(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_active_small(doc) -> - ["Server sends 1000 bytes in active mode to client, that receives them, " - "sends them back, and closes."]; - -client_echos_active_small(suite) -> - []; +client_echos_active_small() -> + [{doc, "Server sends 1000 bytes in active mode to client, that receives them, " + "sends them back, and closes."}]. client_echos_active_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), + ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -271,12 +204,9 @@ client_echos_active_small(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -server_echos_passive_big(doc) -> - ["Client sends 50000 bytes to server in passive mode, that receives them, " - "sends them back, and closes."]; - -server_echos_passive_big(suite) -> - []; +server_echos_passive_big() -> + [{doc, "Client sends 50000 bytes to server in passive mode, that receives them, " + "sends them back, and closes."}]. server_echos_passive_big(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -290,15 +220,12 @@ server_echos_passive_big(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -server_echos_active_once_big(doc) -> - ["Client sends 50000 bytes to server in active once mode, that receives " - "them, sends them back, and closes."]; - -server_echos_active_once_big(suite) -> - []; +server_echos_active_once_big() -> + [{doc,"Client sends 50000 bytes to server in active once mode, that receives " + "them, sends them back, and closes."}]. server_echos_active_once_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), + ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -309,12 +236,9 @@ server_echos_active_once_big(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -server_echos_active_big(doc) -> - ["Client sends 50000 bytes to server in active once mode, that receives " - " them, sends them back, and closes."]; - -server_echos_active_big(suite) -> - []; +server_echos_active_big() -> + [{doc, "Client sends 50000 bytes to server in active once mode, that receives " + " them, sends them back, and closes."}]. server_echos_active_big(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -327,12 +251,9 @@ server_echos_active_big(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_passive_big(doc) -> - ["Server sends 50000 bytes to client in passive mode, that receives them, " - "sends them back, and closes."]; - -client_echos_passive_big(suite) -> - []; +client_echos_passive_big() -> + [{doc, "Server sends 50000 bytes to client in passive mode, that receives them, " + "sends them back, and closes."}]. client_echos_passive_big(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -345,12 +266,9 @@ client_echos_passive_big(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_active_once_big(doc) -> - ["Server sends 50000 bytes to client in active once mode, that receives" - " them, sends them back, and closes."]; - -client_echos_active_once_big(suite) -> - []; +client_echos_active_once_big() -> + [{doc, "Server sends 50000 bytes to client in active once mode, that receives" + " them, sends them back, and closes."}]. client_echos_active_once_big(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -363,12 +281,9 @@ client_echos_active_once_big(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_active_big(doc) -> - ["Server sends 50000 bytes to client in active mode, that receives them, " - "sends them back, and closes."]; - -client_echos_active_big(suite) -> - []; +client_echos_active_big() -> + [{doc, "Server sends 50000 bytes to client in active mode, that receives them, " + "sends them back, and closes."}]. client_echos_active_big(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -381,12 +296,9 @@ client_echos_active_big(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -server_echos_passive_huge(doc) -> - ["Client sends 500000 bytes to server in passive mode, that receives " - " them, sends them back, and closes."]; - -server_echos_passive_huge(suite) -> - []; +server_echos_passive_huge() -> + [{doc, "Client sends 500000 bytes to server in passive mode, that receives " + " them, sends them back, and closes."}]. server_echos_passive_huge(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -399,12 +311,9 @@ server_echos_passive_huge(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -server_echos_active_once_huge(doc) -> - ["Client sends 500000 bytes to server in active once mode, that receives " - "them, sends them back, and closes."]; - -server_echos_active_once_huge(suite) -> - []; +server_echos_active_once_huge() -> + [{doc, "Client sends 500000 bytes to server in active once mode, that receives " + "them, sends them back, and closes."}]. server_echos_active_once_huge(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -417,12 +326,9 @@ server_echos_active_once_huge(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -server_echos_active_huge(doc) -> - ["Client sends 500000 bytes to server in active mode, that receives them, " - "sends them back, and closes."]; - -server_echos_active_huge(suite) -> - []; +server_echos_active_huge() -> + [{doc, "Client sends 500000 bytes to server in active mode, that receives them, " + "sends them back, and closes."}]. server_echos_active_huge(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -435,12 +341,9 @@ server_echos_active_huge(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_passive_huge(doc) -> - ["Server sends 500000 bytes to client in passive mode, that receives " - "them, sends them back, and closes."]; - -client_echos_passive_huge(suite) -> - []; +client_echos_passive_huge() -> + [{doc, "Server sends 500000 bytes to client in passive mode, that receives " + "them, sends them back, and closes."}]. client_echos_passive_huge(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -452,12 +355,9 @@ client_echos_passive_huge(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_active_once_huge(doc) -> - ["Server sends 500000 bytes to client in active once mode, that receives " - "them, sends them back, and closes."]; - -client_echos_active_once_huge(suite) -> - []; +client_echos_active_once_huge() -> + [{doc, "Server sends 500000 bytes to client in active once mode, that receives " + "them, sends them back, and closes."}]. client_echos_active_once_huge(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -469,12 +369,9 @@ client_echos_active_once_huge(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_active_huge(doc) -> - ["Server sends 500000 bytes to client in active mode, that receives them, " - "sends them back, and closes."]; - -client_echos_active_huge(suite) -> - []; +client_echos_active_huge() -> + [{doc, "Server sends 500000 bytes to client in active mode, that receives them, " + "sends them back, and closes."}]. client_echos_active_huge(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -486,6 +383,8 @@ client_echos_active_huge(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- server_echos_passive(Data, Length, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname) -> @@ -656,33 +555,33 @@ send(Socket, Data, Size, Repeate,F) -> sender(Socket, Data, Size) -> ok = send(Socket, Data, Size, 100, fun() -> do_recv(Socket, Data, Size, <<>>, false) end), - test_server:format("Sender recv: ~p~n", [ssl:getopts(Socket, [active])]), + ct:log("Sender recv: ~p~n", [ssl:getopts(Socket, [active])]), ok. sender_once(Socket, Data, Size) -> send(Socket, Data, Size, 100, fun() -> do_active_once(Socket, Data, Size, <<>>, false) end), - test_server:format("Sender active once: ~p~n", + ct:log("Sender active once: ~p~n", [ssl:getopts(Socket, [active])]), ok. sender_active(Socket, Data, Size) -> F = fun() -> do_active(Socket, Data, Size, <<>>, false) end, send(Socket, Data, Size, 100, F), - test_server:format("Sender active: ~p~n", [ssl:getopts(Socket, [active])]), + ct:log("Sender active: ~p~n", [ssl:getopts(Socket, [active])]), ok. echoer(Socket, Data, Size) -> - test_server:format("Echoer recv: ~p~n", [ssl:getopts(Socket, [active])]), + ct:log("Echoer recv: ~p~n", [ssl:getopts(Socket, [active])]), echo(fun() -> do_recv(Socket, Data, Size, <<>>, true) end, 100). echoer_once(Socket, Data, Size) -> - test_server:format("Echoer active once: ~p ~n", + ct:log("Echoer active once: ~p ~n", [ssl:getopts(Socket, [active])]), echo(fun() -> do_active_once(Socket, Data, Size, <<>>, true) end, 100). echoer_active(Socket, Data, Size) -> - test_server:format("Echoer active: ~p~n", [ssl:getopts(Socket, [active])]), + ct:log("Echoer active: ~p~n", [ssl:getopts(Socket, [active])]), echo(fun() -> do_active(Socket, Data, Size, <<>>, true) end, 100). echo(_Fun, 0) -> ok; diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index 1d71efd40c..c31f6c2d7d 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2012. All Rights Reserved. +%% Copyright Ericsson AB 2010-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -37,29 +37,31 @@ -export([init/1, terminate/1, lookup/2, update/3, delete/2, foldl/3, select_session/2]). -%% Test server callback functions %%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initialization before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [session_cleanup, + session_cache_process_list, + session_cache_process_mnesia]. + +groups() -> + []. + init_per_suite(Config0) -> - Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), + Dog = ct:timetrap(?LONG_TIMEOUT *2), catch crypto:stop(), try crypto:start() of ok -> - application:start(public_key), ssl:start(), - %% make rsa certs using oppenssl Result = (catch make_certs:all(?config(data_dir, Config0), ?config(priv_dir, Config0))), - test_server:format("Make certs ~p~n", [Result]), + ct:log("Make certs ~p~n", [Result]), Config1 = ssl_test_lib:make_dsa_cert(Config0), Config = ssl_test_lib:cert_options(Config1), @@ -68,29 +70,16 @@ init_per_suite(Config0) -> {skip, "Crypto did not start"} end. -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- end_per_suite(_Config) -> ssl:stop(), application:stop(crypto). -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initialization before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initialization before each test case -%%-------------------------------------------------------------------- +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + init_per_testcase(session_cache_process_list, Config) -> init_customized_session_cache(list, Config); @@ -100,7 +89,7 @@ init_per_testcase(session_cache_process_mnesia, Config) -> init_per_testcase(session_cleanup, Config0) -> Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), + Dog = ct:timetrap(?TIMEOUT), ssl:stop(), application:load(ssl), application:set_env(ssl, session_lifetime, 5), @@ -110,12 +99,12 @@ init_per_testcase(session_cleanup, Config0) -> init_per_testcase(_TestCase, Config0) -> Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), + Dog = ct:timetrap(?TIMEOUT), [{watchdog, Dog} | Config]. init_customized_session_cache(Type, Config0) -> Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), + Dog = ct:timetrap(?TIMEOUT), ssl:stop(), application:load(ssl), application:set_env(ssl, session_cb, ?MODULE), @@ -123,14 +112,6 @@ init_customized_session_cache(Type, Config0) -> ssl:start(), [{watchdog, Dog} | Config]. -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- end_per_testcase(session_cache_process_list, Config) -> application:unset_env(ssl, session_cb), end_per_testcase(default_action, Config); @@ -146,43 +127,14 @@ end_per_testcase(session_cleanup, Config) -> application:unset_env(ssl, session_lifetime), end_per_testcase(default_action, Config); end_per_testcase(_TestCase, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end. - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. - -all() -> - [session_cleanup, - session_cache_process_list, - session_cache_process_mnesia]. - -groups() -> - []. - -init_per_group(_GroupName, Config) -> Config. -end_per_group(_GroupName, Config) -> - Config. %%-------------------------------------------------------------------- -session_cleanup(doc) -> - ["Test that sessions are cleand up eventually, so that the session table " - "does not grow and grow ..."]; -session_cleanup(suite) -> - []; +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +session_cleanup() -> + [{doc, "Test that sessions are cleand up eventually, so that the session table " + "does not grow and grow ..."}]. session_cleanup(Config)when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ?config(client_opts, Config), @@ -207,7 +159,7 @@ session_cleanup(Config)when is_list(Config) -> end, %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), [_, _,_, _, Prop] = StatusInfo, @@ -224,14 +176,14 @@ session_cleanup(Config)when is_list(Config) -> %% Make sure session has expired and been cleaned up check_timer(SessionTimer), - test_server:sleep(?DELAY *2), %% Delay time + some extra time + ct:sleep(?DELAY *2), %% Delay time + some extra time {ServerDelayTimer, ClientDelayTimer} = get_delay_timers(), check_timer(ServerDelayTimer), check_timer(ClientDelayTimer), - test_server:sleep(?SLEEP), %% Make sure clean has had time to run + ct:sleep(?SLEEP), %% Make sure clean has had time to run undefined = ssl_session_cache:lookup(Cache, {{Hostname, Port}, Id}), undefined = ssl_session_cache:lookup(Cache, {Port, Id}), @@ -248,7 +200,7 @@ check_timer(Timer) -> {status, _, _, _} = sys:get_status(whereis(ssl_manager)), ok; Int -> - test_server:sleep(Int), + ct:sleep(Int), check_timer(Timer) end. @@ -258,31 +210,25 @@ get_delay_timers() -> State = ssl_test_lib:state(Prop), case element(7, State) of {undefined, undefined} -> - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), get_delay_timers(); {undefined, _} -> - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), get_delay_timers(); {_, undefined} -> - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), get_delay_timers(); DelayTimers -> DelayTimers end. %%-------------------------------------------------------------------- -session_cache_process_list(doc) -> - ["Test reuse of sessions (short handshake)"]; - -session_cache_process_list(suite) -> - []; +session_cache_process_list() -> + [{doc,"Test reuse of sessions (short handshake)"}]. session_cache_process_list(Config) when is_list(Config) -> session_cache_process(list,Config). %%-------------------------------------------------------------------- -session_cache_process_mnesia(doc) -> - ["Test reuse of sessions (short handshake)"]; - -session_cache_process_mnesia(suite) -> - []; +session_cache_process_mnesia() -> + [{doc,"Test reuse of sessions (short handshake)"}]. session_cache_process_mnesia(Config) when is_list(Config) -> session_cache_process(mnesia,Config). diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index f1f5b9ae0a..34c52b10b3 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -20,8 +20,7 @@ %% -module(ssl_test_lib). --include("test_server.hrl"). --include("test_server_line.hrl"). +-include_lib("common_test/include/ct.hrl"). -include_lib("public_key/include/public_key.hrl"). %% Note: This directive should only be used in test suites. @@ -29,12 +28,6 @@ -record(sslsocket, { fd = nil, pid = nil}). -timetrap(Time) -> - Mul = try - test_server:timetrap_scale_factor() - catch _:_ -> 1 end, - test_server:timetrap(1000+Time*Mul). - %% For now always run locally run_where(_) -> ClientNode = node(), @@ -65,8 +58,9 @@ run_server(Opts) -> Port = proplists:get_value(port, Opts), Options = proplists:get_value(options, Opts), Pid = proplists:get_value(from, Opts), - test_server:format("ssl:listen(~p, ~p)~n", [Port, Options]), - {ok, ListenSocket} = rpc:call(Node, ssl, listen, [Port, Options]), + Transport = proplists:get_value(transport, Opts, ssl), + ct:log("ssl:listen(~p, ~p)~n", [Port, Options]), + {ok, ListenSocket} = rpc:call(Node, Transport, listen, [Port, Options]), Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), run_server(ListenSocket, Opts). @@ -81,14 +75,15 @@ do_run_server(_, {error, timeout} = Result, Opts) -> do_run_server(ListenSocket, AcceptSocket, Opts) -> Node = proplists:get_value(node, Opts), Pid = proplists:get_value(from, Opts), + Transport = proplists:get_value(transport, Opts, ssl), {Module, Function, Args} = proplists:get_value(mfa, Opts), - test_server:format("Server: apply(~p,~p,~p)~n", + ct:log("Server: apply(~p,~p,~p)~n", [Module, Function, [AcceptSocket | Args]]), case rpc:call(Node, Module, Function, [AcceptSocket | Args]) of no_result_msg -> ok; Msg -> - test_server:format("Server Msg: ~p ~n", [Msg]), + ct:log("Server Msg: ~p ~n", [Msg]), Pid ! {self(), Msg} end, receive @@ -97,15 +92,16 @@ do_run_server(ListenSocket, AcceptSocket, Opts) -> {listen, MFA} -> run_server(ListenSocket, [MFA | proplists:delete(mfa, Opts)]); close -> - test_server:format("Server closing ~p ~n", [self()]), - Result = rpc:call(Node, ssl, close, [AcceptSocket], 500), - test_server:format("Result ~p ~n", [Result]); + ct:log("Server closing ~p ~n", [self()]), + Result = rpc:call(Node, Transport, close, [AcceptSocket], 500), + Result1 = rpc:call(Node, Transport, close, [ListenSocket], 500), + ct:log("Result ~p : ~p ~n", [Result, Result1]); {ssl_closed, _} -> ok end. %%% To enable to test with s_client -reconnect -connect(ListenSocket, Opts) -> +connect(#sslsocket{} = ListenSocket, Opts) -> Node = proplists:get_value(node, Opts), ReconnectTimes = proplists:get_value(reconnect_times, Opts, 0), Timeout = proplists:get_value(timeout, Opts, infinity), @@ -116,15 +112,21 @@ connect(ListenSocket, Opts) -> _ -> remove_close_msg(ReconnectTimes), AcceptSocket - end. - + end; +connect(ListenSocket, Opts) -> + Node = proplists:get_value(node, Opts), + ct:log("gen_tcp:accept(~p)~n", [ListenSocket]), + {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, + [ListenSocket]), + AcceptSocket. + connect(_, _, 0, AcceptSocket, _) -> AcceptSocket; connect(ListenSocket, Node, N, _, Timeout) -> - test_server:format("ssl:transport_accept(~p)~n", [ListenSocket]), + ct:log("ssl:transport_accept(~p)~n", [ListenSocket]), {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept, [ListenSocket]), - test_server:format("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, Timeout]), + ct:log("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, Timeout]), case rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Timeout]) of ok -> @@ -157,45 +159,48 @@ run_client(Opts) -> Host = proplists:get_value(host, Opts), Port = proplists:get_value(port, Opts), Pid = proplists:get_value(from, Opts), + Transport = proplists:get_value(transport, Opts, ssl), Options = proplists:get_value(options, Opts), - test_server:format("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]), - case rpc:call(Node, ssl, connect, [Host, Port, Options]) of + ct:log("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]), + case rpc:call(Node, Transport, connect, [Host, Port, Options]) of {ok, Socket} -> Pid ! { connected, Socket }, - test_server:format("Client: connected~n", []), + ct:log("Client: connected~n", []), %% In special cases we want to know the client port, it will %% be indicated by sending {port, 0} in options list! send_selected_port(Pid, proplists:get_value(port, Options), Socket), {Module, Function, Args} = proplists:get_value(mfa, Opts), - test_server:format("Client: apply(~p,~p,~p)~n", + ct:log("Client: apply(~p,~p,~p)~n", [Module, Function, [Socket | Args]]), case rpc:call(Node, Module, Function, [Socket | Args]) of no_result_msg -> ok; Msg -> - test_server:format("Client Msg: ~p ~n", [Msg]), + ct:log("Client Msg: ~p ~n", [Msg]), Pid ! {self(), Msg} end, receive close -> - test_server:format("Client closing~n", []), - rpc:call(Node, ssl, close, [Socket]); + ct:log("Client closing~n", []), + rpc:call(Node, Transport, close, [Socket]); {ssl_closed, Socket} -> + ok; + {gen_tcp, closed} -> ok end; {error, Reason} -> - test_server:format("Client: connection failed: ~p ~n", [Reason]), + ct:log("Client: connection failed: ~p ~n", [Reason]), Pid ! {self(), {error, Reason}} end. close(Pid) -> - test_server:format("Close ~p ~n", [Pid]), + ct:log("Close ~p ~n", [Pid]), Monitor = erlang:monitor(process, Pid), Pid ! close, receive {'DOWN', Monitor, process, Pid, Reason} -> erlang:demonitor(Monitor), - test_server:format("Pid: ~p down due to:~p ~n", [Pid, Reason]) + ct:log("Pid: ~p down due to:~p ~n", [Pid, Reason]) end. check_result(Server, ServerMsg, Client, ClientMsg) -> @@ -207,7 +212,7 @@ check_result(Server, ServerMsg, Client, ClientMsg) -> Unexpected -> Reason = {{expected, {Client, ClientMsg}}, {got, Unexpected}}, - test_server:fail(Reason) + ct:fail(Reason) end; {Client, ClientMsg} -> receive @@ -216,7 +221,7 @@ check_result(Server, ServerMsg, Client, ClientMsg) -> Unexpected -> Reason = {{expected, {Server, ClientMsg}}, {got, Unexpected}}, - test_server:fail(Reason) + ct:fail(Reason) end; {Port, {data,Debug}} when is_port(Port) -> io:format("openssl ~s~n",[Debug]), @@ -225,7 +230,7 @@ check_result(Server, ServerMsg, Client, ClientMsg) -> Unexpected -> Reason = {{expected, {Client, ClientMsg}}, {expected, {Server, ServerMsg}}, {got, Unexpected}}, - test_server:fail(Reason) + ct:fail(Reason) end. check_result(Pid, Msg) -> @@ -238,7 +243,7 @@ check_result(Pid, Msg) -> Unexpected -> Reason = {{expected, {Pid, Msg}}, {got, Unexpected}}, - test_server:fail(Reason) + ct:fail(Reason) end. wait_for_result(Server, ServerMsg, Client, ClientMsg) -> @@ -276,6 +281,13 @@ wait_for_result(Pid, Msg) -> %% Unexpected end. +user_lookup(psk, _Identity, UserState) -> + {ok, UserState}; +user_lookup(srp, Username, _UserState) -> + Salt = ssl:random_bytes(16), + UserPassHash = crypto:hash(sha, [Salt, crypto:hash(sha, [Username, <<$:>>, <<"secret">>])]), + {ok, {srp_1024, Salt, UserPassHash}}. + cert_options(Config) -> ClientCaCertFile = filename:join([?config(priv_dir, Config), "client", "cacerts.pem"]), @@ -302,6 +314,7 @@ cert_options(Config) -> "badcert.pem"]), BadKeyFile = filename:join([?config(priv_dir, Config), "badkey.pem"]), + PskSharedSecret = <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>, [{client_opts, [{ssl_imp, new},{reuseaddr, true}]}, {client_verification_opts, [{cacertfile, ClientCaCertFile}, {certfile, ClientCertFile}, @@ -314,6 +327,34 @@ cert_options(Config) -> {server_opts, [{ssl_imp, new},{reuseaddr, true}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, {server_anon, [{ssl_imp, new},{reuseaddr, true}, {ciphers, anonymous_suites()}]}, + {client_psk, [{ssl_imp, new},{reuseaddr, true}, + {psk_identity, "Test-User"}, + {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]}, + {server_psk, [{ssl_imp, new},{reuseaddr, true}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}, + {ciphers, psk_suites()}]}, + {server_psk_hint, [{ssl_imp, new},{reuseaddr, true}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {psk_identity, "HINT"}, + {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}, + {ciphers, psk_suites()}]}, + {server_psk_anon, [{ssl_imp, new},{reuseaddr, true}, + {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}, + {ciphers, psk_anon_suites()}]}, + {server_psk_anon_hint, [{ssl_imp, new},{reuseaddr, true}, + {psk_identity, "HINT"}, + {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}, + {ciphers, psk_anon_suites()}]}, + {client_srp, [{ssl_imp, new},{reuseaddr, true}, + {srp_identity, {"Test-User", "secret"}}]}, + {server_srp, [{ssl_imp, new},{reuseaddr, true}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {user_lookup_fun, {fun user_lookup/3, undefined}}, + {ciphers, srp_suites()}]}, + {server_srp_anon, [{ssl_imp, new},{reuseaddr, true}, + {user_lookup_fun, {fun user_lookup/3, undefined}}, + {ciphers, srp_anon_suites()}]}, {server_verification_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, @@ -351,9 +392,63 @@ make_dsa_cert(Config) -> {verify, verify_peer}]}, {client_dsa_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ClientCaCertFile}, - {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]} + {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]}, + {server_srp_dsa, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ServerCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {user_lookup_fun, {fun user_lookup/3, undefined}}, + {ciphers, srp_dss_suites()}]}, + {client_srp_dsa, [{ssl_imp, new},{reuseaddr, true}, + {srp_identity, {"Test-User", "secret"}}, + {cacertfile, ClientCaCertFile}, + {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]} | Config]. +make_ecdsa_cert(Config) -> + CryptoSupport = crypto:supports(), + case proplists:get_bool(ecdsa, proplists:get_value(public_keys, CryptoSupport)) of + true -> + {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, ec, ec, ""), + {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_cert_files("client", Config, ec, ec, ""), + [{server_ecdsa_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ServerCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, + {server_ecdsa_verify_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ClientCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {verify, verify_peer}]}, + {client_ecdsa_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ClientCaCertFile}, + {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]} + | Config]; + _ -> + Config + end. + +%% RFC 4492, Sect. 2.3. ECDH_RSA +%% +%% This key exchange algorithm is the same as ECDH_ECDSA except that the +%% server's certificate MUST be signed with RSA rather than ECDSA. +make_ecdh_rsa_cert(Config) -> + CryptoSupport = crypto:supports(), + case proplists:get_bool(ecdh, proplists:get_value(public_keys, CryptoSupport)) of + true -> + {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, rsa, ec, "rsa_"), + {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_cert_files("client", Config, rsa, ec, "rsa_"), + [{server_ecdh_rsa_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ServerCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, + {server_ecdh_rsa_verify_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ClientCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {verify, verify_peer}]}, + {client_ecdh_rsa_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ClientCaCertFile}, + {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]} + | Config]; + _ -> + Config + end. make_mix_cert(Config) -> {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, dsa, @@ -405,33 +500,33 @@ run_upgrade_server(Opts) -> SslOptions = proplists:get_value(ssl_options, Opts), Pid = proplists:get_value(from, Opts), - test_server:format("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), + ct:log("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), {ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]), Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), - test_server:format("gen_tcp:accept(~p)~n", [ListenSocket]), + ct:log("gen_tcp:accept(~p)~n", [ListenSocket]), {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]), try {ok, SslAcceptSocket} = case TimeOut of infinity -> - test_server:format("ssl:ssl_accept(~p, ~p)~n", + ct:log("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, SslOptions]), rpc:call(Node, ssl, ssl_accept, [AcceptSocket, SslOptions]); _ -> - test_server:format("ssl:ssl_accept(~p, ~p, ~p)~n", + ct:log("ssl:ssl_accept(~p, ~p, ~p)~n", [AcceptSocket, SslOptions, TimeOut]), rpc:call(Node, ssl, ssl_accept, [AcceptSocket, SslOptions, TimeOut]) end, {Module, Function, Args} = proplists:get_value(mfa, Opts), Msg = rpc:call(Node, Module, Function, [SslAcceptSocket | Args]), - test_server:format("Upgrade Server Msg: ~p ~n", [Msg]), + ct:log("Upgrade Server Msg: ~p ~n", [Msg]), Pid ! {self(), Msg}, receive close -> - test_server:format("Upgrade Server closing~n", []), + ct:log("Upgrade Server closing~n", []), rpc:call(Node, ssl, close, [SslAcceptSocket]) end catch error:{badmatch, Error} -> @@ -449,24 +544,24 @@ run_upgrade_client(Opts) -> TcpOptions = proplists:get_value(tcp_options, Opts), SslOptions = proplists:get_value(ssl_options, Opts), - test_server:format("gen_tcp:connect(~p, ~p, ~p)~n", + ct:log("gen_tcp:connect(~p, ~p, ~p)~n", [Host, Port, TcpOptions]), {ok, Socket} = rpc:call(Node, gen_tcp, connect, [Host, Port, TcpOptions]), send_selected_port(Pid, Port, Socket), - test_server:format("ssl:connect(~p, ~p)~n", [Socket, SslOptions]), + ct:log("ssl:connect(~p, ~p)~n", [Socket, SslOptions]), {ok, SslSocket} = rpc:call(Node, ssl, connect, [Socket, SslOptions]), {Module, Function, Args} = proplists:get_value(mfa, Opts), - test_server:format("apply(~p, ~p, ~p)~n", + ct:log("apply(~p, ~p, ~p)~n", [Module, Function, [SslSocket | Args]]), Msg = rpc:call(Node, Module, Function, [SslSocket | Args]), - test_server:format("Upgrade Client Msg: ~p ~n", [Msg]), + ct:log("Upgrade Client Msg: ~p ~n", [Msg]), Pid ! {self(), Msg}, receive close -> - test_server:format("Upgrade Client closing~n", []), + ct:log("Upgrade Client closing~n", []), rpc:call(Node, ssl, close, [SslSocket]) end. @@ -485,20 +580,20 @@ run_upgrade_server_error(Opts) -> SslOptions = proplists:get_value(ssl_options, Opts), Pid = proplists:get_value(from, Opts), - test_server:format("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), + ct:log("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), {ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]), Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), - test_server:format("gen_tcp:accept(~p)~n", [ListenSocket]), + ct:log("gen_tcp:accept(~p)~n", [ListenSocket]), {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]), Error = case TimeOut of infinity -> - test_server:format("ssl:ssl_accept(~p, ~p)~n", + ct:log("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, SslOptions]), rpc:call(Node, ssl, ssl_accept, [AcceptSocket, SslOptions]); _ -> - test_server:format("ssl:ssl_accept(~p, ~p, ~p)~n", + ct:log("ssl:ssl_accept(~p, ~p, ~p)~n", [AcceptSocket, SslOptions, TimeOut]), rpc:call(Node, ssl, ssl_accept, [AcceptSocket, SslOptions, TimeOut]) @@ -517,22 +612,31 @@ run_server_error(Opts) -> Port = proplists:get_value(port, Opts), Options = proplists:get_value(options, Opts), Pid = proplists:get_value(from, Opts), - test_server:format("ssl:listen(~p, ~p)~n", [Port, Options]), - case rpc:call(Node, ssl, listen, [Port, Options]) of - {ok, ListenSocket} -> + Transport = proplists:get_value(transport, Opts, ssl), + ct:log("ssl:listen(~p, ~p)~n", [Port, Options]), + case rpc:call(Node, Transport, listen, [Port, Options]) of + {ok, #sslsocket{} = ListenSocket} -> %% To make sure error_client will %% get {error, closed} and not {error, connection_refused} Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), - test_server:format("ssl:transport_accept(~p)~n", [ListenSocket]), - case rpc:call(Node, ssl, transport_accept, [ListenSocket]) of + ct:log("ssl:transport_accept(~p)~n", [ListenSocket]), + case rpc:call(Node, Transport, transport_accept, [ListenSocket]) of {error, _} = Error -> Pid ! {self(), Error}; {ok, AcceptSocket} -> - test_server:format("ssl:ssl_accept(~p)~n", [AcceptSocket]), + ct:log("ssl:ssl_accept(~p)~n", [AcceptSocket]), Error = rpc:call(Node, ssl, ssl_accept, [AcceptSocket]), Pid ! {self(), Error} end; + {ok, ListenSocket} -> + Pid ! {listen, up}, + send_selected_port(Pid, Port, ListenSocket), + ct:log("~p:accept(~p)~n", [Transport, ListenSocket]), + case rpc:call(Node, Transport, accept, [ListenSocket]) of + {error, _} = Error -> + Pid ! {self(), Error} + end; Error -> %% Not really true but as this is an error test %% this is what we want. @@ -548,9 +652,10 @@ run_client_error(Opts) -> Host = proplists:get_value(host, Opts), Port = proplists:get_value(port, Opts), Pid = proplists:get_value(from, Opts), + Transport = proplists:get_value(transport, Opts, ssl), Options = proplists:get_value(options, Opts), - test_server:format("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]), - Error = rpc:call(Node, ssl, connect, [Host, Port, Options]), + ct:log("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]), + Error = rpc:call(Node, Transport, connect, [Host, Port, Options]), Pid ! {self(), Error}. inet_port(Pid) when is_pid(Pid)-> @@ -577,7 +682,7 @@ trigger_renegotiate(Socket, [ErlData, N]) -> trigger_renegotiate(Socket, ErlData, N, Id). trigger_renegotiate(Socket, _, 0, Id) -> - test_server:sleep(1000), + ct:sleep(1000), case ssl:session_info(Socket) of [{session_id, Id} | _ ] -> fail_session_not_renegotiated; @@ -606,11 +711,16 @@ send_selected_port(Pid, 0, Socket) -> send_selected_port(_,_,_) -> ok. -rsa_suites() -> - lists:filter(fun({dhe_dss, _, _}) -> - false; +rsa_suites(CounterPart) -> + ECC = is_sane_ecc(CounterPart), + lists:filter(fun({rsa, _, _}) -> + true; + ({dhe_rsa, _, _}) -> + true; + ({ecdhe_rsa, _, _}) when ECC == true -> + true; (_) -> - true + false end, ssl:cipher_suites()). @@ -630,17 +740,38 @@ dsa_suites() -> end, ssl:cipher_suites()). +ecdsa_suites() -> + lists:filter(fun({ecdhe_ecdsa, _, _}) -> + true; + (_) -> + false + end, + ssl:cipher_suites()). -openssl_rsa_suites() -> +ecdh_rsa_suites() -> + lists:filter(fun({ecdh_rsa, _, _}) -> + true; + (_) -> + false + end, + ssl:cipher_suites()). + +openssl_rsa_suites(CounterPart) -> Ciphers = ssl:cipher_suites(openssl), + Names = case is_sane_ecc(CounterPart) of + true -> + "DSS | ECDSA"; + false -> + "DSS | ECDHE | ECDH" + end, lists:filter(fun(Str) -> - case re:run(Str,"DSS",[]) of + case re:run(Str, Names,[]) of nomatch -> - true; + false; _ -> - false + true end - end, Ciphers). + end, Ciphers). openssl_dsa_suites() -> Ciphers = ssl:cipher_suites(openssl), @@ -653,12 +784,88 @@ openssl_dsa_suites() -> end end, Ciphers). +openssl_ecdsa_suites() -> + Ciphers = ssl:cipher_suites(openssl), + lists:filter(fun(Str) -> + case re:run(Str,"ECDHE-ECDSA",[]) of + nomatch -> + false; + _ -> + true + end + end, Ciphers). + +openssl_ecdh_rsa_suites() -> + Ciphers = ssl:cipher_suites(openssl), + lists:filter(fun(Str) -> + case re:run(Str,"ECDH-RSA",[]) of + nomatch -> + false; + _ -> + true + end + end, Ciphers). + anonymous_suites() -> - [{dh_anon, rc4_128, md5}, - {dh_anon, des_cbc, sha}, - {dh_anon, '3des_ede_cbc', sha}, - {dh_anon, aes_128_cbc, sha}, - {dh_anon, aes_256_cbc, sha}]. + Suites = + [{dh_anon, rc4_128, md5}, + {dh_anon, des_cbc, sha}, + {dh_anon, '3des_ede_cbc', sha}, + {dh_anon, aes_128_cbc, sha}, + {dh_anon, aes_256_cbc, sha}, + {ecdh_anon,rc4_128,sha}, + {ecdh_anon,'3des_ede_cbc',sha}, + {ecdh_anon,aes_128_cbc,sha}, + {ecdh_anon,aes_256_cbc,sha}], + ssl_cipher:filter_suites(Suites). + +psk_suites() -> + Suites = + [{psk, rc4_128, sha}, + {psk, '3des_ede_cbc', sha}, + {psk, aes_128_cbc, sha}, + {psk, aes_256_cbc, sha}, + {dhe_psk, rc4_128, sha}, + {dhe_psk, '3des_ede_cbc', sha}, + {dhe_psk, aes_128_cbc, sha}, + {dhe_psk, aes_256_cbc, sha}, + {rsa_psk, rc4_128, sha}, + {rsa_psk, '3des_ede_cbc', sha}, + {rsa_psk, aes_128_cbc, sha}, + {rsa_psk, aes_256_cbc, sha}], + ssl_cipher:filter_suites(Suites). + +psk_anon_suites() -> + [{psk, rc4_128, sha}, + {psk, '3des_ede_cbc', sha}, + {psk, aes_128_cbc, sha}, + {psk, aes_256_cbc, sha}, + {dhe_psk, rc4_128, sha}, + {dhe_psk, '3des_ede_cbc', sha}, + {dhe_psk, aes_128_cbc, sha}, + {dhe_psk, aes_256_cbc, sha}]. + +srp_suites() -> + Suites = + [{srp_anon, '3des_ede_cbc', sha}, + {srp_rsa, '3des_ede_cbc', sha}, + {srp_anon, aes_128_cbc, sha}, + {srp_rsa, aes_128_cbc, sha}, + {srp_anon, aes_256_cbc, sha}, + {srp_rsa, aes_256_cbc, sha}], + ssl_cipher:filter_suites(Suites). + +srp_anon_suites() -> + [{srp_anon, '3des_ede_cbc', sha}, + {srp_anon, aes_128_cbc, sha}, + {srp_anon, aes_256_cbc, sha}]. + +srp_dss_suites() -> + Suites = + [{srp_dss, '3des_ede_cbc', sha}, + {srp_dss, aes_128_cbc, sha}, + {srp_dss, aes_256_cbc, sha}], + ssl_cipher:filter_suites(Suites). pem_to_der(File) -> {ok, PemBin} = file:read_file(File), @@ -670,7 +877,7 @@ der_to_pem(File, Entries) -> cipher_result(Socket, Result) -> Result = ssl:connection_info(Socket), - test_server:format("Successfull connect: ~p~n", [Result]), + ct:log("Successfull connect: ~p~n", [Result]), %% Importante to send two packets here %% to properly test "cipher state" handling ssl:send(Socket, "Hello\n"), @@ -740,14 +947,82 @@ init_tls_version(Version) -> ssl:start(). sufficient_crypto_support('tlsv1.2') -> - Data = "Sampl", - Data2 = "e #1", - Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39, - 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>, - try - crypto:sha256_mac(Key, lists:flatten([Data, Data2])), - true - catch _:_ -> false - end; + CryptoSupport = crypto:supports(), + proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)); +sufficient_crypto_support(ciphers_ec) -> + CryptoSupport = crypto:supports(), + proplists:get_bool(ecdh, proplists:get_value(public_keys, CryptoSupport)); sufficient_crypto_support(_) -> true. + +send_recv_result_active(Socket) -> + ssl:send(Socket, "Hello world"), + receive + {ssl, Socket, "H"} -> + receive + {ssl, Socket, "ello world"} -> + ok + end; + {ssl, Socket, "Hello world"} -> + ok + end. + +send_recv_result(Socket) -> + ssl:send(Socket, "Hello world"), + {ok,"Hello world"} = ssl:recv(Socket, 11), + ok. + +send_recv_result_active_once(Socket) -> + ssl:send(Socket, "Hello world"), + receive + {ssl, Socket, "H"} -> + ssl:setopts(Socket, [{active, once}]), + receive + {ssl, Socket, "ello world"} -> + ok + end; + {ssl, Socket, "Hello world"} -> + ok + end. + +is_sane_ecc(openssl) -> + case os:cmd("openssl version") of + "OpenSSL 1.0.0a" ++ _ -> % Known bug in openssl + %% manifests as SSL_CHECK_SERVERHELLO_TLSEXT:tls invalid ecpointformat list + false; + "OpenSSL 1.0.0" ++ _ -> % Known bug in openssl + %% manifests as SSL_CHECK_SERVERHELLO_TLSEXT:tls invalid ecpointformat list + false; + "OpenSSL 0.9.8" ++ _ -> % Does not support ECC + false; + "OpenSSL 0.9.7" ++ _ -> % Does not support ECC + false; + _ -> + true + end; +is_sane_ecc(crypto) -> + [{_,_, Bin}] = crypto:info_lib(), + case binary_to_list(Bin) of + "OpenSSL 0.9.8" ++ _ -> % Does not support ECC + false; + "OpenSSL 0.9.7" ++ _ -> % Does not support ECC + false; + _ -> + true + end; +is_sane_ecc(_) -> + true. + +cipher_restriction(Config0) -> + case is_sane_ecc(openssl) of + false -> + Opts = proplists:get_value(server_opts, Config0), + Config1 = proplists:delete(server_opts, Config0), + VerOpts = proplists:get_value(server_verification_opts, Config1), + Config = proplists:delete(server_verification_opts, Config1), + Restricted0 = ssl:cipher_suites() -- ecdsa_suites(), + Restricted = Restricted0 -- ecdh_rsa_suites(), + [{server_opts, [{ciphers, Restricted} | Opts]}, {server_verification_opts, [{ciphers, Restricted} | VerOpts] } | Config]; + true -> + Config0 + end. diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index d446014f7b..019ed58b1b 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -16,7 +16,6 @@ %% %% %CopyrightEnd% %% - %% -module(ssl_to_openssl_SUITE). @@ -29,125 +28,15 @@ -define(TIMEOUT, 120000). -define(LONG_TIMEOUT, 600000). -define(SLEEP, 1000). --define(OPENSSL_RENEGOTIATE, "r\n"). +-define(OPENSSL_RENEGOTIATE, "R\n"). -define(OPENSSL_QUIT, "Q\n"). -define(OPENSSL_GARBAGE, "P\n"). -define(EXPIRE, 10). -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initialization before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config0) -> - Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), - case os:find_executable("openssl") of - false -> - {skip, "Openssl not found"}; - _ -> - catch crypto:stop(), - try crypto:start() of - ok -> - application:start(public_key), - ssl:start(), - Result = - (catch make_certs:all(?config(data_dir, Config0), - ?config(priv_dir, Config0))), - test_server:format("Make certs ~p~n", [Result]), - Config1 = ssl_test_lib:make_dsa_cert(Config0), - Config = ssl_test_lib:cert_options(Config1), - [{watchdog, Dog} | Config] - catch _:_ -> - {skip, "Crypto did not start"} - end - end. - %%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- -end_per_suite(_Config) -> - ssl:stop(), - application:stop(crypto). -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initialization before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initialization before each test case -%%-------------------------------------------------------------------- -init_per_testcase(expired_session, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5), - ssl:stop(), - application:load(ssl), - application:set_env(ssl, session_lifetime, ?EXPIRE), - ssl:start(), - [{watchdog, Dog} | Config]; - -init_per_testcase(TestCase, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = ssl_test_lib:timetrap(?TIMEOUT), - special_init(TestCase, [{watchdog, Dog} | Config]). - -special_init(TestCase, Config) - when TestCase == erlang_client_openssl_server_renegotiate; - TestCase == erlang_client_openssl_server_no_wrap_sequence_number; - TestCase == erlang_server_openssl_client_no_wrap_sequence_number - -> - check_sane_openssl_renegotaite(Config); - -special_init(ssl2_erlang_server_openssl_client, Config) -> - check_sane_openssl_sslv2(Config); - -special_init(_, Config) -> - Config. - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(reuse_session_expired, Config) -> - application:unset_env(ssl, session_lifetime), - end_per_testcase(default_action, Config); - -end_per_testcase(default_action, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end; -end_per_testcase(_, Config) -> - end_per_testcase(default_action, Config). - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> @@ -161,9 +50,9 @@ all() -> groups() -> [{basic, [], basic_tests()}, - {'tlsv1.2', [], all_versions_tests()}, - {'tlsv1.1', [], all_versions_tests()}, - {'tlsv1', [], all_versions_tests()}, + {'tlsv1.2', [], all_versions_tests() ++ npn_tests()}, + {'tlsv1.1', [], all_versions_tests() ++ npn_tests()}, + {'tlsv1', [], all_versions_tests()++ npn_tests()}, {'sslv3', [], all_versions_tests()}]. basic_tests() -> @@ -179,16 +68,54 @@ all_versions_tests() -> erlang_server_openssl_client_dsa_cert, erlang_server_openssl_client_reuse_session, erlang_client_openssl_server_renegotiate, - erlang_client_openssl_server_no_wrap_sequence_number, - erlang_server_openssl_client_no_wrap_sequence_number, + erlang_client_openssl_server_nowrap_seqnum, + erlang_server_openssl_client_nowrap_seqnum, erlang_client_openssl_server_no_server_ca_cert, erlang_client_openssl_server_client_cert, erlang_server_openssl_client_client_cert, ciphers_rsa_signed_certs, ciphers_dsa_signed_certs, erlang_client_bad_openssl_server, - ssl2_erlang_server_openssl_client - ]. + expired_session, + ssl2_erlang_server_openssl_client]. + +npn_tests() -> + [erlang_client_openssl_server_npn, + erlang_server_openssl_client_npn, + erlang_server_openssl_client_npn_renegotiate, + erlang_client_openssl_server_npn_renegotiate, + erlang_server_openssl_client_npn_only_client, + erlang_server_openssl_client_npn_only_server, + erlang_client_openssl_server_npn_only_client, + erlang_client_openssl_server_npn_only_server]. + + +init_per_suite(Config0) -> + Dog = ct:timetrap(?LONG_TIMEOUT *2), + case os:find_executable("openssl") of + false -> + {skip, "Openssl not found"}; + _ -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl:start(), + Result = + (catch make_certs:all(?config(data_dir, Config0), + ?config(priv_dir, Config0))), + ct:log("Make certs ~p~n", [Result]), + Config1 = ssl_test_lib:make_dsa_cert(Config0), + Config2 = ssl_test_lib:cert_options(Config1), + Config = [{watchdog, Dog} | Config2], + ssl_test_lib:cipher_restriction(Config) + catch _:_ -> + {skip, "Crypto did not start"} + end + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). init_per_group(GroupName, Config) -> case ssl_test_lib:is_tls_version(GroupName) of @@ -208,13 +135,55 @@ init_per_group(GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +init_per_testcase(expired_session, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ct:timetrap(?EXPIRE * 1000 * 5), + ssl:stop(), + application:load(ssl), + application:set_env(ssl, session_lifetime, ?EXPIRE), + ssl:start(), + [{watchdog, Dog} | Config]; + +init_per_testcase(TestCase, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ct:timetrap(?TIMEOUT), + special_init(TestCase, [{watchdog, Dog} | Config]). + +special_init(TestCase, Config) + when TestCase == erlang_client_openssl_server_renegotiate; + TestCase == erlang_client_openssl_server_nowrap_seqnum; + TestCase == erlang_server_openssl_client_nowrap_seqnum + -> + check_sane_openssl_renegotaite(Config); + +special_init(ssl2_erlang_server_openssl_client, Config) -> + check_sane_openssl_sslv2(Config); + +special_init(TestCase, Config) + when TestCase == erlang_client_openssl_server_npn; + TestCase == erlang_server_openssl_client_npn; + TestCase == erlang_server_openssl_client_npn_renegotiate; + TestCase == erlang_client_openssl_server_npn_renegotiate; + TestCase == erlang_server_openssl_client_npn_only_server; + TestCase == erlang_server_openssl_client_npn_only_client; + TestCase == erlang_client_openssl_server_npn_only_client; + TestCase == erlang_client_openssl_server_npn_only_server -> + check_openssl_npn_support(Config); + +special_init(_, Config) -> + Config. + +end_per_testcase(reuse_session_expired, Config) -> + application:unset_env(ssl, session_lifetime), + Config; +end_per_testcase(_, Config) -> + Config. -%% Test cases starts here. %%-------------------------------------------------------------------- -basic_erlang_client_openssl_server(doc) -> - ["Test erlang client with openssl server"]; -basic_erlang_client_openssl_server(suite) -> - []; +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +basic_erlang_client_openssl_server() -> + [{doc,"Test erlang client with openssl server"}]. basic_erlang_client_openssl_server(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -231,7 +200,7 @@ basic_erlang_client_openssl_server(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile, - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -243,21 +212,18 @@ basic_erlang_client_openssl_server(Config) when is_list(Config) -> {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ClientOpts}]), - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Client, ok), %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -basic_erlang_server_openssl_client(doc) -> - ["Test erlang server with openssl client"]; -basic_erlang_server_openssl_client(suite) -> - []; +basic_erlang_server_openssl_client() -> + [{doc,"Test erlang server with openssl client"}]. basic_erlang_server_openssl_client(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -275,10 +241,10 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) -> Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ " -host localhost", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), + true = port_command(OpenSslPort, Data), ssl_test_lib:check_result(Server, ok), @@ -288,10 +254,8 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) -> process_flag(trap_exit, false), ok. %%-------------------------------------------------------------------- -erlang_client_openssl_server(doc) -> - ["Test erlang client with openssl server"]; -erlang_client_openssl_server(suite) -> - []; +erlang_client_openssl_server() -> + [{doc,"Test erlang client with openssl server"}]. erlang_client_openssl_server(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -304,11 +268,11 @@ erlang_client_openssl_server(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile, - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -320,22 +284,18 @@ erlang_client_openssl_server(Config) when is_list(Config) -> {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ClientOpts}]), - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Client, ok), %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. - + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_server_openssl_client(doc) -> - ["Test erlang server with openssl client"]; -erlang_server_openssl_client(suite) -> - []; +erlang_server_openssl_client() -> + [{doc,"Test erlang server with openssl client"}]. erlang_server_openssl_client(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -349,30 +309,27 @@ erlang_server_openssl_client(Config) when is_list(Config) -> {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), + true = port_command(OpenSslPort, Data), ssl_test_lib:check_result(Server, ok), %% Clean close down! Server needs to be closed first !! ssl_test_lib:close(Server), close_port(OpenSslPort), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_client_openssl_server_dsa_cert(doc) -> - ["Test erlang server with openssl client"]; -erlang_client_openssl_server_dsa_cert(suite) -> - []; +erlang_client_openssl_server_dsa_cert() -> + [{doc,"Test erlang server with openssl client"}]. erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ?config(client_dsa_opts, Config), @@ -386,13 +343,13 @@ erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> CaCertFile = proplists:get_value(cacertfile, ServerOpts), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile ++ " -key " ++ KeyFile ++ " -Verify 2 -msg", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -405,7 +362,7 @@ erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> erlang_ssl_receive, [Data]}}, {options, ClientOpts}]), - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Client, ok), @@ -415,10 +372,8 @@ erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> process_flag(trap_exit, false), ok. %%-------------------------------------------------------------------- -erlang_server_openssl_client_dsa_cert(doc) -> - ["Test erlang server with openssl client"]; -erlang_server_openssl_client_dsa_cert(suite) -> - []; +erlang_server_openssl_client_dsa_cert() -> + [{doc,"Test erlang server with openssl client"}]. erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ?config(client_dsa_opts, Config), @@ -436,31 +391,28 @@ erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile ++ " -key " ++ KeyFile ++ " -msg", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), + true = port_command(OpenSslPort, Data), ssl_test_lib:check_result(Server, ok), %% Clean close down! Server needs to be closed first !! ssl_test_lib:close(Server), close_port(OpenSslPort), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_server_openssl_client_reuse_session(doc) -> - ["Test erlang server with openssl client that reconnects with the" - "same session id, to test reusing of sessions."]; -erlang_server_openssl_client_reuse_session(suite) -> - []; +erlang_server_openssl_client_reuse_session() -> + [{doc, "Test erlang server with openssl client that reconnects with the" + "same session id, to test reusing of sessions."}]. erlang_server_openssl_client_reuse_session(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -475,15 +427,15 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) -> {reconnect_times, 5}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost -reconnect", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), + true = port_command(OpenSslPort, Data), ssl_test_lib:check_result(Server, ok), @@ -495,10 +447,8 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -erlang_client_openssl_server_renegotiate(doc) -> - ["Test erlang client when openssl server issuses a renegotiate"]; -erlang_client_openssl_server_renegotiate(suite) -> - []; +erlang_client_openssl_server_renegotiate() -> + [{doc,"Test erlang client when openssl server issuses a renegotiate"}]. erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -512,12 +462,12 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -530,9 +480,9 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> delayed_send, [[ErlData, OpenSslData]]}}, {options, ClientOpts}]), - port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), - test_server:sleep(?SLEEP), - port_command(OpensslPort, OpenSslData), + true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), + ct:sleep(?SLEEP), + true = port_command(OpensslPort, OpenSslData), ssl_test_lib:check_result(Client, ok), @@ -544,14 +494,12 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -erlang_client_openssl_server_no_wrap_sequence_number(doc) -> - ["Test that erlang client will renegotiate session when", +erlang_client_openssl_server_nowrap_seqnum() -> + [{doc, "Test that erlang client will renegotiate session when", "max sequence number celing is about to be reached. Although" "in the testcase we use the test option renegotiate_at" - " to lower treashold substantially."]; -erlang_client_openssl_server_no_wrap_sequence_number(suite) -> - []; -erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config) -> + " to lower treashold substantially."}]. +erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -564,11 +512,11 @@ erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -587,18 +535,14 @@ erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_server_openssl_client_no_wrap_sequence_number(doc) -> - ["Test that erlang client will renegotiate session when", +erlang_server_openssl_client_nowrap_seqnum() -> + [{doc, "Test that erlang client will renegotiate session when", "max sequence number celing is about to be reached. Although" "in the testcase we use the test option renegotiate_at" - " to lower treashold substantially."]; - -erlang_server_openssl_client_no_wrap_sequence_number(suite) -> - []; -erlang_server_openssl_client_no_wrap_sequence_number(Config) when is_list(Config) -> + " to lower treashold substantially."}]. +erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -614,31 +558,29 @@ erlang_server_openssl_client_no_wrap_sequence_number(Config) when is_list(Config trigger_renegotiate, [[Data, N+2]]}}, {options, [{renegotiate_at, N}, {reuse_sessions, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), - Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost -msg", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), + true = port_command(OpenSslPort, Data), ssl_test_lib:check_result(Server, ok), %% Clean close down! Server needs to be closed first !! ssl_test_lib:close(Server), close_port(OpenSslPort), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). + %%-------------------------------------------------------------------- -erlang_client_openssl_server_no_server_ca_cert(doc) -> - ["Test erlang client when openssl server sends a cert chain not" +erlang_client_openssl_server_no_server_ca_cert() -> + [{doc, "Test erlang client when openssl server sends a cert chain not" "including the ca cert. Explicitly test this even if it is" - "implicitly tested eleswhere."]; -erlang_client_openssl_server_no_server_ca_cert(suite) -> - []; + "implicitly tested eleswhere."}]. erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -651,11 +593,11 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -668,21 +610,18 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) -> erlang_ssl_receive, [Data]}}, {options, ClientOpts}]), - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Client, ok), %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_client_openssl_server_client_cert(doc) -> - ["Test erlang client with openssl server when client sends cert"]; -erlang_client_openssl_server_client_cert(suite) -> - []; +erlang_client_openssl_server_client_cert() -> + [{doc,"Test erlang client with openssl server when client sends cert"}]. erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_verification_opts, Config), @@ -696,12 +635,12 @@ erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> CertFile = proplists:get_value(certfile, ServerOpts), CaCertFile = proplists:get_value(cacertfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile ++ " -key " ++ KeyFile ++ " -Verify 2", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -713,22 +652,19 @@ erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ClientOpts}]), - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Client, ok), %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_server_openssl_client_client_cert(doc) -> - ["Test erlang server with openssl client when client sends cert"]; -erlang_server_openssl_client_client_cert(suite) -> - []; +erlang_server_openssl_client_client_cert() -> + [{doc,"Test erlang server with openssl client when client sends cert"}]. erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_verification_opts, Config), @@ -750,36 +686,32 @@ erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> CaCertFile = proplists:get_value(cacertfile, ClientOpts), CertFile = proplists:get_value(certfile, ClientOpts), KeyFile = proplists:get_value(keyfile, ClientOpts), - Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Cmd = "openssl s_client -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), + true = port_command(OpenSslPort, Data), ssl_test_lib:check_result(Server, ok), %% Clean close down! Server needs to be closed first !! close_port(OpenSslPort), ssl_test_lib:close(Server), - process_flag(trap_exit, false), - ok. - + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_server_erlang_client_client_cert(doc) -> - ["Test erlang server with erlang client when client sends cert"]; -erlang_server_erlang_client_client_cert(suite) -> - []; +erlang_server_erlang_client_client_cert() -> + [{doc,"Test erlang server with erlang client when client sends cert"}]. erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_verification_opts, Config), ClientOpts = ?config(client_verification_opts, Config), - Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Data = "From erlang to erlang", @@ -807,121 +739,32 @@ erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. -%%-------------------------------------------------------------------- + process_flag(trap_exit, false). -ciphers_rsa_signed_certs(doc) -> - ["Test cipher suites that uses rsa certs"]; - -ciphers_rsa_signed_certs(suite) -> - []; +%%-------------------------------------------------------------------- +ciphers_rsa_signed_certs() -> + [{doc,"Test cipher suites that uses rsa certs"}]. ciphers_rsa_signed_certs(Config) when is_list(Config) -> Version = - ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + tls_record:protocol_version(tls_record:highest_protocol_version([])), - Ciphers = ssl_test_lib:rsa_suites(), + Ciphers = ssl_test_lib:rsa_suites(openssl), run_suites(Ciphers, Version, Config, rsa). +%%-------------------------------------------------------------------- - -ciphers_dsa_signed_certs(doc) -> - ["Test cipher suites that uses dsa certs"]; - -ciphers_dsa_signed_certs(suite) -> - []; - +ciphers_dsa_signed_certs() -> + [{doc,"Test cipher suites that uses dsa certs"}]. ciphers_dsa_signed_certs(Config) when is_list(Config) -> Version = - ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + tls_record:protocol_version(tls_record:highest_protocol_version([])), Ciphers = ssl_test_lib:dsa_suites(), run_suites(Ciphers, Version, Config, dsa). -run_suites(Ciphers, Version, Config, Type) -> - {ClientOpts, ServerOpts} = - case Type of - rsa -> - {?config(client_opts, Config), - ?config(server_opts, Config)}; - dsa -> - {?config(client_opts, Config), - ?config(server_dsa_opts, Config)} - end, - - Result = lists:map(fun(Cipher) -> - cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end, - Ciphers), - case lists:flatten(Result) of - [] -> - ok; - Error -> - test_server:format("Cipher suite errors: ~p~n", [Error]), - test_server:fail(cipher_suite_failed_see_test_case_log) - end. - -cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> - process_flag(trap_exit, true), - test_server:format("Testing CipherSuite ~p~n", [CipherSuite]), - {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ - " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", - - test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - - wait_for_openssl_server(), - - ConnectionInfo = {ok, {Version, CipherSuite}}, - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, - {options, - [{ciphers,[CipherSuite]} | - ClientOpts]}]), - - port_command(OpenSslPort, "Hello\n"), - - receive - {Port, {data, _}} when is_port(Port) -> - ok - after 500 -> - test_server:format("Time out on openssl port, check that" - " the messages Hello and world are received" - " during close of port" , []), - ok - end, - - port_command(OpenSslPort, " world\n"), - - Result = ssl_test_lib:wait_for_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - close_port(OpenSslPort), - ssl_test_lib:close(Client), - - Return = case Result of - ok -> - []; - Error -> - [{CipherSuite, Error}] - end, - process_flag(trap_exit, false), - Return. - %%-------------------------------------------------------------------- -erlang_client_bad_openssl_server(doc) -> - [""]; -erlang_client_bad_openssl_server(suite) -> - []; +erlang_client_bad_openssl_server() -> + [{doc,"Test what happens if openssl server sends garbage to erlang ssl client"}]. erlang_client_bad_openssl_server(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_verification_opts, Config), @@ -932,11 +775,11 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -950,9 +793,9 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> [{versions, [Version]} | ClientOpts]}]), %% Send garbage - port_command(OpensslPort, ?OPENSSL_GARBAGE), + true = port_command(OpensslPort, ?OPENSSL_GARBAGE), - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), Client0 ! server_sent_garbage, @@ -976,13 +819,9 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -expired_session(doc) -> - ["Test our ssl client handling of expired sessions. Will make" - "better code coverage of the ssl_manager module"]; - -expired_session(suite) -> - []; - +expired_session() -> + [{doc, "Test our ssl client handling of expired sessions. Will make" + "better code coverage of the ssl_manager module"}]. expired_session(Config) when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ?config(client_opts, Config), @@ -996,7 +835,7 @@ expired_session(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -1011,7 +850,7 @@ expired_session(Config) when is_list(Config) -> ssl_test_lib:close(Client0), %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), Client1 = ssl_test_lib:start_client([{node, ClientNode}, @@ -1021,7 +860,7 @@ expired_session(Config) when is_list(Config) -> ssl_test_lib:close(Client1), %% Make sure session is unregistered due to expiration - test_server:sleep((?EXPIRE+1) * 1000), + ct:sleep((?EXPIRE+1) * 1000), Client2 = ssl_test_lib:start_client([{node, ClientNode}, @@ -1035,10 +874,9 @@ expired_session(Config) when is_list(Config) -> process_flag(trap_exit, false). %%-------------------------------------------------------------------- -ssl2_erlang_server_openssl_client(doc) -> - ["Test that ssl v2 clients are rejected"]; -ssl2_erlang_server_openssl_client(suite) -> - []; +ssl2_erlang_server_openssl_client() -> + [{doc,"Test that ssl v2 clients are rejected"}]. + ssl2_erlang_server_openssl_client(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -1055,23 +893,339 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) -> Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ " -host localhost -ssl2 -msg", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), - - ssl_test_lib:check_result(Server, {error,"protocol version"}), - + true = port_command(OpenSslPort, Data), + receive + {'EXIT', OpenSslPort, _} -> + ok + + end, + ssl_test_lib:check_result(Server, {error, {tls_alert, "protocol version"}}), + process_flag(trap_exit, false). + +%%-------------------------------------------------------------------- +erlang_client_openssl_server_npn() -> + [{doc,"Test erlang client with openssl server doing npn negotiation"}]. + +erlang_client_openssl_server_npn(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) -> + true = port_command(OpensslPort, Data), + + ssl_test_lib:check_result(Client, ok) + end), + ok. + +%%-------------------------------------------------------------------- +erlang_client_openssl_server_npn_renegotiate() -> + [{doc,"Test erlang client with openssl server doing npn negotiation and renegotiate"}]. + +erlang_client_openssl_server_npn_renegotiate(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) -> + true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), + ct:sleep(?SLEEP), + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Client, ok) + end), + ok. +%%-------------------------------------------------------------------------- +erlang_server_openssl_client_npn() -> + [{doc,"Test erlang server with openssl client and npn negotiation"}]. + +erlang_server_openssl_client_npn(Config) when is_list(Config) -> + + Data = "From openssl to erlang", + start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, ok) + end), + ok. + +%%-------------------------------------------------------------------------- +erlang_server_openssl_client_npn_renegotiate() -> + [{doc,"Test erlang server with openssl client and npn negotiation with renegotiation"}]. + +erlang_server_openssl_client_npn_renegotiate(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) -> + true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), + ct:sleep(?SLEEP), + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, ok) + end), + ok. +%%-------------------------------------------------------------------------- +erlang_client_openssl_server_npn_only_server(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_client_and_openssl_server_with_opts(Config, [], + "-nextprotoneg spdy/2", Data, fun(Server, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, ok) + end), + ok. + +%%-------------------------------------------------------------------------- + +erlang_client_openssl_server_npn_only_client(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_client_and_openssl_server_with_opts(Config, + [{client_preferred_next_protocols, + {client, [<<"spdy/2">>], <<"http/1.1">>}}], "", + Data, fun(Server, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, ok) + end), + ok. + +%%-------------------------------------------------------------------------- +erlang_server_openssl_client_npn_only_server(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_server_and_openssl_client_with_opts(Config, [{next_protocols_advertised, [<<"spdy/2">>]}], "", + Data, fun(Server, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, ok) + end), + ok. + +erlang_server_openssl_client_npn_only_client(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_server_and_openssl_client_with_opts(Config, [], "-nextprotoneg spdy/2", + Data, fun(Server, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, ok) + end), + ok. + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- +run_suites(Ciphers, Version, Config, Type) -> + {ClientOpts, ServerOpts} = + case Type of + rsa -> + {?config(client_opts, Config), + ?config(server_opts, Config)}; + dsa -> + {?config(client_opts, Config), + ?config(server_dsa_opts, Config)} + end, + + Result = lists:map(fun(Cipher) -> + cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end, + Ciphers), + case lists:flatten(Result) of + [] -> + ok; + Error -> + ct:log("Cipher suite errors: ~p~n", [Error]), + ct:fail(cipher_suite_failed_see_test_case_log) + end. + +cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> + process_flag(trap_exit, true), + ct:log("Testing CipherSuite ~p~n", [CipherSuite]), + {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ + " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", + + ct:log("openssl cmd: ~p~n", [Cmd]), + + OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + + wait_for_openssl_server(), + + ConnectionInfo = {ok, {Version, CipherSuite}}, + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, + {options, + [{ciphers,[CipherSuite]} | + ClientOpts]}]), + + true = port_command(OpenSslPort, "Hello\n"), + + receive + {Port, {data, _}} when is_port(Port) -> + ok + after 500 -> + ct:log("Time out on openssl port, check that" + " the messages Hello and world are received" + " during close of port" , []), + ok + end, + + true = port_command(OpenSslPort, " world\n"), + + Result = ssl_test_lib:wait_for_result(Client, ok), + %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), close_port(OpenSslPort), + ssl_test_lib:close(Client), + + Return = case Result of + ok -> + []; + Error -> + [{CipherSuite, Error}] + end, process_flag(trap_exit, false), - ok. + Return. -%%-------------------------------------------------------------------- +start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, OpensslServerOpts, Data, Callback) -> + process_flag(trap_exit, true), + ServerOpts = ?config(server_opts, Config), + ClientOpts0 = ?config(client_opts, Config), + ClientOpts = ErlangClientOpts ++ ClientOpts0, + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + + Cmd = "openssl s_server " ++ OpensslServerOpts ++ " -accept " ++ + integer_to_list(Port) ++ version_flag(Version) ++ + " -cert " ++ CertFile ++ " -key " ++ KeyFile, + + ct:log("openssl cmd: ~p~n", [Cmd]), + + OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + + wait_for_openssl_server(), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + erlang_ssl_receive, [Data]}}, + {options, ClientOpts}]), + + Callback(Client, OpensslPort), + + %% Clean close down! Server needs to be closed first !! + close_port(OpensslPort), + + ssl_test_lib:close(Client), + process_flag(trap_exit, false). + +start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callback) -> + process_flag(trap_exit, true), + ServerOpts = ?config(server_opts, Config), + ClientOpts0 = ?config(client_opts, Config), + ClientOpts = [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}} | ClientOpts0], + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + + Cmd = "openssl s_server -msg -nextprotoneg http/1.1,spdy/2 -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ + " -cert " ++ CertFile ++ " -key " ++ KeyFile, + + ct:log("openssl cmd: ~p~n", [Cmd]), + + OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + + wait_for_openssl_server(), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + erlang_ssl_receive_and_assert_npn, [<<"spdy/2">>, Data]}}, + {options, ClientOpts}]), + + Callback(Client, OpensslPort), + + %% Clean close down! Server needs to be closed first !! + close_port(OpensslPort), + + ssl_test_lib:close(Client), + process_flag(trap_exit, false). + +start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callback) -> + process_flag(trap_exit, true), + ServerOpts0 = ?config(server_opts, Config), + ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>]}, ServerOpts0], + + {_, ServerNode, _} = ssl_test_lib:run_where(Config), + + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, erlang_ssl_receive_and_assert_npn, [<<"spdy/2">>, Data]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + Cmd = "openssl s_client -nextprotoneg http/1.0,spdy/2 -msg -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ + " -host localhost", + + ct:log("openssl cmd: ~p~n", [Cmd]), + + OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + + Callback(Server, OpenSslPort), + + ssl_test_lib:close(Server), + + close_port(OpenSslPort), + process_flag(trap_exit, false). + + +start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenSSLClientOpts, Data, Callback) -> + process_flag(trap_exit, true), + ServerOpts0 = ?config(server_opts, Config), + ServerOpts = ErlangServerOpts ++ ServerOpts0, + + {_, ServerNode, _} = ssl_test_lib:run_where(Config), + + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Version = tls_record:protocol_version(tls_record:highest_protocol_version([])), + Cmd = "openssl s_client " ++ OpenSSLClientOpts ++ " -msg -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ + " -host localhost", + + ct:log("openssl cmd: ~p~n", [Cmd]), + + OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + + Callback(Server, OpenSslPort), + + ssl_test_lib:close(Server), + + close_port(OpenSslPort), + process_flag(trap_exit, false). + + +erlang_ssl_receive_and_assert_npn(Socket, Protocol, Data) -> + {ok, Protocol} = ssl:negotiated_next_protocol(Socket), + erlang_ssl_receive(Socket, Data), + {ok, Protocol} = ssl:negotiated_next_protocol(Socket), + ok. erlang_ssl_receive(Socket, Data) -> - test_server:format("Connection info: ~p~n", + ct:log("Connection info: ~p~n", [ssl:connection_info(Socket)]), receive {ssl, Socket, Data} -> @@ -1085,15 +1239,15 @@ erlang_ssl_receive(Socket, Data) -> io:format("openssl ~s~n",[Debug]), erlang_ssl_receive(Socket,Data); Other -> - test_server:fail({unexpected_message, Other}) + ct:fail({unexpected_message, Other}) after 4000 -> - test_server:fail({did_not_get, Data}) + ct:fail({did_not_get, Data}) end. connection_info(Socket, Version) -> case ssl:connection_info(Socket) of {ok, {Version, _} = Info} -> - test_server:format("Connection info: ~p~n", [Info]), + ct:log("Connection info: ~p~n", [Info]), ok; {ok, {OtherVersion, _}} -> {wrong_version, OtherVersion} @@ -1104,7 +1258,7 @@ connection_info_result(Socket) -> delayed_send(Socket, [ErlData, OpenSslData]) -> - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), ssl:send(Socket, ErlData), erlang_ssl_receive(Socket, OpenSslData). @@ -1115,28 +1269,28 @@ close_port(Port) -> close_loop(Port, Time, SentClose) -> receive {Port, {data,Debug}} when is_port(Port) -> - io:format("openssl ~s~n",[Debug]), + ct:log("openssl ~s~n",[Debug]), close_loop(Port, Time, SentClose); {ssl,_,Msg} -> - io:format("ssl Msg ~s~n",[Msg]), + ct:log("ssl Msg ~s~n",[Msg]), close_loop(Port, Time, SentClose); {Port, closed} -> - io:format("Port Closed~n",[]), + ct:log("Port Closed~n",[]), ok; {'EXIT', Port, Reason} -> - io:format("Port Closed ~p~n",[Reason]), + ct:log("Port Closed ~p~n",[Reason]), ok; Msg -> - io:format("Port Msg ~p~n",[Msg]), + ct:log("Port Msg ~p~n",[Msg]), close_loop(Port, Time, SentClose) after Time -> case SentClose of false -> - io:format("Closing port ~n",[]), + ct:log("Closing port ~n",[]), catch erlang:port_close(Port), close_loop(Port, Time, true); true -> - io:format("Timeout~n",[]) + ct:log("Timeout~n",[]) end end. @@ -1151,12 +1305,12 @@ server_sent_garbage(Socket) -> wait_for_openssl_server() -> receive {Port, {data, Debug}} when is_port(Port) -> - io:format("openssl ~s~n",[Debug]), + ct:log("openssl ~s~n",[Debug]), %% openssl has started make sure %% it will be in accept. Parsing %% output is too error prone. (Even %% more so than sleep!) - test_server:sleep(?SLEEP) + ct:sleep(?SLEEP) end. version_flag(tlsv1) -> @@ -1168,6 +1322,15 @@ version_flag('tlsv1.2') -> version_flag(sslv3) -> " -ssl3 ". +check_openssl_npn_support(Config) -> + HelpText = os:cmd("openssl s_client --help"), + case string:str(HelpText, "nextprotoneg") of + 0 -> + {skip, "Openssl not compiled with nextprotoneg support"}; + _ -> + Config + end. + check_sane_openssl_renegotaite(Config) -> case os:cmd("openssl version") of "OpenSSL 0.9.8" ++ _ -> @@ -1179,11 +1342,27 @@ check_sane_openssl_renegotaite(Config) -> end. check_sane_openssl_sslv2(Config) -> - case os:cmd("openssl version") of - "OpenSSL 1." ++ _ -> - {skip, "sslv2 by default turned of in 1.*"}; - _ -> - Config + Port = open_port({spawn, "openssl s_client -ssl2 "}, [stderr_to_stdout]), + case supports_sslv2(Port) of + true -> + Config; + false -> + {skip, "sslv2 not supported by openssl"} + end. + +supports_sslv2(Port) -> + receive + {Port, {data, "unknown option -ssl2" ++ _}} -> + false; + {Port, {data, Data}} -> + case lists:member("error", string:tokens(Data, ":")) of + true -> + false; + false -> + supports_sslv2(Port) + end + after 500 -> + true end. check_sane_openssl_version(Version) -> diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 84728fd311..9dd151553c 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 5.1.2.1 +SSL_VSN = 5.3 |