Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Usage problem with Crypt::DH::GMP::priv_key( )

by Llew_Llaw_Gyffes (Scribe)
on Mar 07, 2016 at 18:24 UTC ( [id://1157023]=perlquestion: print w/replies, xml ) Need Help??

Llew_Llaw_Gyffes has asked for the wisdom of the Perl Monks concerning the following question:

Brethren,

I have a threaded Perl ICB client which features client-side end-to-end encryption. It's been working fine for years using Crypt::DH and Crypt::Blowfish (plus supporting modules of course). Crypt::DH is used to create a shared one-time key for Blowfish. I just tried to update my application to use Crypt::DH::GMP, in order to improve performance and reduce the number of module dependencies, and I'm running into an error that I don't fully understand.

Here are the relevant bits of code:

use Module::Load::Conditional qw[can_load check_install requires]; $Module::Load::Conditional::VERBOSE = 1; my $crypt_mods = {'Crypt::DH::GMP' => undef, 'Crypt::CBC' => undef, 'Crypt::Blowfish' => undef, 'Digest::SHA' => undef, 'MIME::Base64' => undef, 'Compress::Zlib' => undef}; if (can_load(modules => $crypt_mods, verbose => 1)) { $encryption_avail = 1; } my ($DH_public, $DH_public2, $DH_private, $DH_secret); [...] sub create_public_key { my $to = lc($_[0]); my $from = lc($_[1]); my $player = $_[2]; my ($DH, $p, $g, $i, $public, @chars); @chars = split(//,($player ? $to : $from)); $g = ord(shift(@chars)); while (scalar @chars) { $g += ord(shift(@chars)); } @chars = split(//, ($player ? $from : $to)); $i = ord(shift(@chars)); while (scalar @chars) { $i += ord(shift(@chars)); } $i %= scalar(@EICB_PRIMES); $p = $EICB_PRIMES[$i]; $DH = Crypt::DH::GMP->new; $DH->g($g); $DH->p($p); $DH->generate_keys; $DH_public = $DH->pub_key; $DH_private = $DH->priv_key; } sub create_secret_key { my $to = lc($_[0]); my $from = lc($_[1]); my $player = $_[2]; my ($DH, $p, $g, $i, $public, @chars); @chars = split(//,($player ? $to : $from)); $g = ord(shift(@chars)); while (scalar @chars) { $g += ord(shift(@chars)); } @chars = split(//,($player ? $from : $to)); $i = ord(shift(@chars)); while (scalar @chars) { $i += ord(shift(@chars)); } $i %= scalar(@EICB_PRIMES); $p = $EICB_PRIMES[$i]; $DH = Crypt::DH::GMP->new; $DH->g($g); $DH->p($p); $DH->priv_key($DH_private); $DH->generate_keys; $DH_secret = $DH->compute_secret($DH_public2); } [...] sub do_crypt_command { my ($who, $args, $time) = @_; my @args = split(/\s+/, $args); my $command = shift (@args); if ($command == $EICB_DH_INIT) { create_public_key($who, $cur_nick, 0); $connection->sendpriv($who, sprintf('%s%s %s', $EICB_CRYPT_PREFIX, $EICB_DH_REPLY, $DH_public)); } elsif ($command == $EICB_DH_REPLY) { $DH_public2 = shift(@args); create_public_key($who, $cur_nick, 1); $connection->sendpriv($who, sprintf('%s%s %s', $EICB_CRYPT_PREFIX, $EICB_DH_REPLY2, $DH_public)); create_secret_key($who, $cur_nick, 1); } elsif ($command == $EICB_DH_REPLY2) { $DH_public2 = shift(@args); create_secret_key($who, $cur_nick, 0); my $sha1 = Digest::SHA->new; $sha1->add($logbuffer[rand(scalar @logbuffer)]); my $key = $sha1->b64digest; $session_keys{lc($who)} = $key; encrypt(\$key, $cipher, $DH_secret); $connection->sendpriv($who, sprintf('%s%s %s', $EICB_CRYPT_PREFIX, $EICB_SESSION_KEY, $key)); timestamp($time) if ($timestamps_active); icb_print ($output_window, 'sbrkt', "%s", "[="); icb_print ($output_window, 'status', "%s", "SECURE"); icb_print ($output_window, 'sbrkt', "%s", "=]"); icb_print ($output_window, 'status', " Session key for user %s established\n", $who); $tabhist{lc($who)} = time(); } [...]

Logic flow is as follows: When client A requests an encrypted session with client B, A sends B a cleartext message containing an EICB_DH_INIT request. If B has encryption enabled, B calls create_public_key( ) and sends its public key back to A in a EICB_DH_REPLY message. A receives the EICB_DH_REPLY message, calls create_public_key( ) to create its own public key, and sends that back to B in a EICB_DH_REPLY2 message. A and B then both call create_secret_key( ), and B generates a one-time Blowfish session key and sends it back to A encrypted using the shared DH secret.

Using Crypt::DH, all of this works. Using Crypt::DH::GMP, create_public_key( ) works, but create_secret_key( ) fails at the $DH->priv_key($DH_private) statement:

Thread 3 terminated abnormally: Usage: Crypt::DH::GMP::priv_key(dh) at + ./icbm line 2434.

I'm not quite sure what I need to change here. The limited documentation in Crypt::DH::GMP does not make any mention of any change in usage of the priv_key( ) method.

Can anyone point out what I'm doing wrong?

Replies are listed 'Best First'.
Re: Usage problem with Crypt::DH::GMP::priv_key( )
by syphilis (Archbishop) on Mar 08, 2016 at 02:10 UTC
    $DH->priv_key($DH_private);

    When you call priv_key() like that, I believe you provide the function with two args - the string "Crypt::DH::GMP" and $DH_private.

    However, the priv_key() function takes only one argument, and returns a string.
    I presume you therefore need to be calling priv_key() as something like:
    my $string = priv_key($DH_private);
    Afterthought: Or perhaps you'll need to call it by its fully qualified name:
    my $string = Crypt::DH::GMP::priv_key($DH_private);
    However, I know nothing of this module's functionality, and I couldn't find any example of priv_key() usage in either the documentation or the test suite.
    AIUI, the priv_key function is provided in xs/dh_gmp.c as:
    char * PerlCryptDHGMP_priv_key( PerlCryptDHGMP *dh ) { return PerlCryptDHGMP_mpz2sv_str(PerlCryptDHGMP_PRIVKEY_PTR(dh), 1 +0, NULL); }
    Update: And all that essentially does is express the value contained in PerlCryptDHGMP *dh as a base 10 string, and then return that string (packed with extra 0's as needed).

    Cheers,
    Rob
      Actually, the author just replied to me. The problem is an API error in Crypt::DH::GMP, which he is going to fix.

        Immediate fix:

        While priv_key() in Crypt::DH::GMP does not work as a setter (but is supposed to; that's the bug), priv_key can be set in the constructor. So changing:

        $DH = Crypt::DH::GMP->new; $DH->g($g); $DH->p($p); $DH->priv_key($DH_private); $DH->generate_keys;

        to:

        $DH = Crypt::DH::GMP->new(p => $p, g => $g, priv_key => $DH_private); $DH->generate_keys;

        ...solves the problem, and is tidier code anyway.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1157023]
Approved by Corion
Front-paged by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (2)
As of 2024-04-25 06:34 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found