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