From 24fa6df5e1910269d86cb97bb5cef464fc3111ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= Date: Wed, 19 Mar 2014 12:54:22 +0100 Subject: [PATCH] Added support for ECDH key exchange with key SSL_ecdh_curve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is port of following upstream commit to 1.94 version: commit e067e09bf0c6b5844693169af75ec93b22bfa660 Author: Steffen Ullrich Date: Fri Oct 11 18:50:01 2013 +0200 1.955 - added support for ECDH key exchange with key SSL_ecdh_curve Signed-off-by: Petr Písař --- MANIFEST | 1 + SSL.pm | 28 +++++++++++++++++++++++ t/ecdhe.t | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 t/ecdhe.t diff --git a/MANIFEST b/MANIFEST index afed6c6..3d97511 100644 --- a/MANIFEST +++ b/MANIFEST @@ -50,6 +50,7 @@ t/auto_verify_hostname.t t/verify_hostname.t t/sni.t t/mitm.t +t/ecdhe.t util/export_certs.pl META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) diff --git a/SSL.pm b/SSL.pm index bcf098f..ad18f7a 100644 --- a/lib/IO/Socket/SSL.pm +++ b/lib/IO/Socket/SSL.pm @@ -1756,6 +1756,25 @@ sub new { Net::SSLeay::DH_free( $dh ); $rv || return IO::Socket::SSL->error( "Failed to set DH from $f" ); } + + if ( my $curve = $arg_hash->{SSL_ecdh_curve} ) { + return IO::Socket::SSL->error( + "ECDH curve needs Net::SSLeay>=1.56 and OpenSSL>=1.0") + if ! defined( &Net::SSLeay::CTX_set_tmp_ecdh ); + if ( $curve !~ /^\d+$/ ) { + # name of curve, find NID + $curve = Net::SSLeay::OBJ_txt2nid($curve) + || return IO::Socket::SSL->error( + "cannot find NID for curve name '$curve'"); + } + my $ecdh = Net::SSLeay::EC_KEY_new_by_curve_name($curve) or + return IO::Socket::SSL->error( + "cannot create curve for NID $curve"); + Net::SSLeay::CTX_set_tmp_ecdh($ctx,$ecdh) or + return IO::Socket::SSL->error( + "failed to set ECDH curve context"); + Net::SSLeay::EC_KEY_free($ecdh); + } } my $verify_cb = $arg_hash->{SSL_verify_callback}; @@ -2158,11 +2177,20 @@ C option. If you want Diffie-Hellman key exchange you need to supply a suitable file here or use the SSL_dh parameter. See dhparam command in openssl for more information. +To create a server which provides perfect forward secrecy you need to either +give the DH parameters or (better, because faster) the ECDH curve. =item SSL_dh Like SSL_dh_file, but instead of giving a file you use a preloaded or generated DH*. +=item SSL_ecdh_curve + +If you want Elliptic Curve Diffie-Hellmann key exchange you need to supply the +OID or NID of a suitable curve (like 'prime256v1') here. +To create a server which provides perfect forward secrecy you need to either +give the DH parameters or (better, because faster) the ECDH curve. + =item SSL_passwd_cb If your private key is encrypted, you might not want the default password prompt from diff --git a/t/ecdhe.t b/t/ecdhe.t new file mode 100644 index 0000000..40aed59 --- /dev/null +++ b/t/ecdhe.t @@ -0,0 +1,78 @@ +#!perl +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl t/ecdhe.t' + +use strict; +use warnings; +use Net::SSLeay; +use Socket; +use IO::Socket::SSL; + +if ( grep { $^O =~m{$_} } qw( MacOS VOS vmesa riscos amigaos ) ) { + print "1..0 # Skipped: fork not implemented on this platform\n"; + exit +} + +if ( ! defined &Net::SSLeay::CTX_set_tmp_ecdh ) { + print "1..0 # Skipped: no support for ecdh with this openssl/Net::SSLeay\n"; + exit +} + +$|=1; +print "1..4\n"; + +# first create simple ssl-server +my $ID = 'server'; +my $addr = '127.0.0.1'; +my $server = IO::Socket::SSL->new( + LocalAddr => $addr, + Listen => 2, + ReuseAddr => 1, + SSL_cert_file => "certs/server-cert.pem", + SSL_key_file => "certs/server-key.pem", + SSL_ecdh_curve => 'prime256v1', +) || do { + notok($!); + exit +}; +ok("Server Initialization"); + +# add server port to addr +$addr.= ':'.(sockaddr_in( getsockname( $server )))[0]; + +my $pid = fork(); +if ( !defined $pid ) { + die $!; # fork failed + +} elsif ( !$pid ) { ###### Client + + $ID = 'client'; + close($server); + my $to_server = IO::Socket::SSL->new( + PeerAddr => $addr, + SSL_verify_mode => 0 ) || do { + notok( "connect failed: $SSL_ERROR" ); + exit + }; + ok( "client connected" ); + + my $cipher = $to_server->get_cipher(); + if ( $cipher !~m/^ECDHE-/ ) { + notok("bad key exchange: $cipher"); + exit; + } + ok("ecdh key exchange: $cipher"); + +} else { ###### Server + + my $to_client = $server->accept || do { + notok( "accept failed: $SSL_ERROR" ); + kill(9,$pid); + exit; + }; + ok( "Server accepted" ); + wait; +} + +sub ok { print "ok # [$ID] @_\n"; } +sub notok { print "not ok # [$ID] @_\n"; } -- 1.8.5.3