# $id: dh.pm,v 1.19 2009/01/26 01:00:25 turnstep exp $

package Net::SSH::Perl::Kex::DH;
use strict;
use warnings;

use Net::SSH::Perl::Buffer;
use Net::SSH::Perl::Packet;
use Net::SSH::Perl::Constants qw( :msg2 :kex );
use Net::SSH::Perl::Key;

use Carp qw( croak );
use Scalar::Util qw(weaken);

use base qw( Net::SSH::Perl::Kex );

sub new {
    my $class = shift;
    my $ssh = shift;
    my $kex = bless { ssh => $ssh }, $class;
    weaken $kex->{ssh};

sub exchange {
    my $kex = shift;
    my $ssh = $kex->{ssh};
    my $packet;

    $ssh->debug('Generating new Diffie-Hellman Group ' . $kex->group . ' keys');
    my $dh = $kex->_dh_new_group();

    $ssh->debug('Entering Diffie-Hellman Group ' . $kex->group . ' key exchange.');
    $packet = $ssh->packet_start(SSH2_MSG_KEXDH_INIT);
    my $pub_key = $dh->export_key_raw('public');

    $ssh->debug("Sent DH public key, waiting for reply.");
    $packet = Net::SSH::Perl::Packet->read_expect($ssh,

    my $host_key_blob = $packet->get_str;
    my $s_host_key = Net::SSH::Perl::Key->new_from_blob($host_key_blob,
    $ssh->debug("Received host key, type '" . $s_host_key->ssh_name . "'.");


    my $dh_server_pub = $packet->get_mp_int;
    my $signature = $packet->get_str;

    my $dh_server_pub_key = Crypt::PK::DH->new;   
    # create public key object (which will also check the public key for validity)
    $dh_server_pub_key->import_key_raw($dh_server_pub, 'public', $dh->params2hash);

    $ssh->debug("Computing shared secret key.");
    my $shared_secret = $dh->shared_secret($dh_server_pub_key);

    my $hash = $kex->kex_hash(

    $ssh->debug("Verifying server signature.");
    croak "Key verification failed for server host key"
        unless $s_host_key->verify($signature, $hash);


    $kex->derive_keys($hash, $shared_secret, $ssh->session_id);

sub kex_hash {
    my $kex = shift;
    my($c_vs, $s_vs, $c_kexinit, $s_kexinit, $s_host_key_blob,
       $c_dh_pub, $s_dh_pub, $shared_secret) = @_;
    my $b = Net::SSH::Perl::Buffer->new( MP => 'SSH2' );

    $b->put_int32($c_kexinit->length + 1);
    $b->put_int32($s_kexinit->length + 1);



sub derive_key {
    my($kex, $id, $need, $hash, $shared_secret, $session_id) = @_;
    my $b = Net::SSH::Perl::Buffer->new( MP => 'SSH2' );
    my $digest = $kex->hash($b->bytes, $hash, chr($id), $session_id);
    for (my $have = $kex->hash_len; $need > $have; $have += $kex->hash_len) {
        $digest .= $kex->hash($b->bytes, $hash, $digest);


=head1 NAME

Net::SSH::Perl::Kex::DH - Diffie-Hellman Group Agnostic Key Exchange

    # This class should not be used directly, but rather as a base for DH1,
    # DH14SHA1, DH16SHA512, etc 

    use Net::SSH::Perl::Kex::DH;
    use base qw( Net::SSH::Perl::Kex::DH );

    # Simply implement _dh_new_group and return the Crypt::DH group
    sub _dh_new_group {
        my $kex = shift;


I<Net::SSH::Perl::Kex::DH> implements Diffie-Hellman Group Agnostic
Exchange for I<Net::SSH::Perl>. It is a subclass of

Key Exchange uses the Diffie-Hellman key exchange algorithm
to produce a shared secret key between client and server, without
ever sending the shared secret over the insecure network. All that is
sent are the client and server public keys.


Please see the Net::SSH::Perl manpage for author, copyright, and
license information.