]> git.99rst.org Git - irssi-scripts.irssi.org.git/commitdiff
cap_sasl 1.10: add "/sasl pubkey"
authorMantas Mikulėnas <redacted>
Wed, 7 Jan 2015 05:47:22 +0000 (07:47 +0200)
committerMantas Mikulėnas <redacted>
Wed, 7 Jan 2015 06:04:09 +0000 (08:04 +0200)
As users can now generate keys without an external tool, add a command
to dump its public key. (Also useful for verifying that keys generated
*with* external tools are supported – in particular, Crypt::PK::ECC
currently does not support named parameters.)

Misc changes:
- mech ECDSA-NIST256P-CHALLENGE: rename $k to $f
- /sasl keygen: adjust error messages

_data/scripts.yaml
scripts/cap_sasl.pl

index 0e48e8aba6b8b9680cfe3cb3e77e10dfaca42e5b..85520af042609aed0295e57092324f35539cbfc4 100644 (file)
   contact: "grawity@gmail.com"
   description: "Implements SASL authentication (and multi-prefix as free bonus)"
   filename: "cap_sasl.pl"
-  modified: "2014-12-18 22:58:00"
+  modified: "2015-01-07 06:03"
   modules: "Crypt::PK::ECC MIME::Base64"
   license: "GPLv2"
   name: "cap_sasl"
   url: "https://nullroute.eu.org/~grawity/irssi.html"
-  version: "1.9"
+  version: "1.10"
 
 - authors: "ZaMz0n"
   commands: "cddb"
index a9dc23734a7f081f480f453e509a8c1d0167b67b..7c1fba2bf3260adab12b7885aefc769f01669555 100644 (file)
@@ -4,7 +4,7 @@ use MIME::Base64;
 use vars qw($VERSION %IRSSI);
 use constant CHALLENGE_SIZE => 32;
 
-$VERSION = "1.9";
+$VERSION = "1.10";
 %IRSSI = (
     authors     => 'Michael Tharp (gxti), Jilles Tjoelker (jilles), Mantas Mikulėnas (grawity)',
     contact     => 'grawity@gmail.com',
@@ -17,6 +17,15 @@ $VERSION = "1.9";
 my %sasl_auth = ();
 my %mech = ();
 
+sub irssi_abspath {
+       my $f = shift;
+       $f =~ s!^~/!$ENV{HOME}/!;
+       if ($f !~ m!^/!) {
+               $f = Irssi::get_irssi_dir()."/".$f;
+       }
+       return $f;
+}
+
 sub timeout;
 
 sub server_connected {
@@ -269,20 +278,20 @@ $mech{EXTERNAL} = sub {
 };
 
 if (eval {require Crypt::PK::ECC}) {
+       my $mech = "ECDSA-NIST256P-CHALLENGE";
+
        $mech{'ECDSA-NIST256P-CHALLENGE'} = sub {
                my ($sasl, $data) = @_;
                my $u = $sasl->{user};
-               my $k = $sasl->{password};
-               if ($k !~ m!^/!) {
-                       $k = Irssi::get_irssi_dir()."/".$k;
-               }
-               if (!-f $k) {
-                       Irssi::print("SASL: key file '$k' not found", MSGLEVEL_CLIENTERROR);
+               my $f = $sasl->{password};
+               $f = irssi_abspath($f);
+               if (!-f $f) {
+                       Irssi::print("SASL: key file '$f' not found", MSGLEVEL_CLIENTERROR);
                        return;
                }
-               my $pk = eval {Crypt::PK::ECC->new($k)};
+               my $pk = eval {Crypt::PK::ECC->new($f)};
                if ($@ || !$pk || !$pk->is_private) {
-                       Irssi::print("SASL: no private key in file '$k'", MSGLEVEL_CLIENTERROR);
+                       Irssi::print("SASL: no private key in file '$f'", MSGLEVEL_CLIENTERROR);
                        return;
                }
                my $step = ++$sasl->{step};
@@ -308,20 +317,14 @@ if (eval {require Crypt::PK::ECC}) {
        sub cmd_sasl_keygen {
                my ($data, $server, $witem) = @_;
 
-               my $mech = "ECDSA-NIST256P-CHALLENGE";
-               my $net;
-               my $print;
-
-               if ($server) {
-                       $net = $server->{tag};
-                       $print = sub { $server->print("", shift) };
-               } else {
-                       $net = $data;
-                       $print = sub { Irssi::print(shift) };
-               }
+               my $print = $server
+                               ? sub { $server->print("", shift, shift // MSGLEVEL_CLIENTNOTICE) }
+                               : sub { Irssi::print(shift, shift // MSGLEVEL_CLIENTNOTICE) };
 
+               my $net = $server ? $server->{tag} : $data;
                if (!length $net) {
-                       $print->("Please connect to a server first.");
+                       Irssi::print("SASL: please connect to a server first",
+                                               MSGLEVEL_CLIENTERROR);
                        return;
                }
 
@@ -330,7 +333,7 @@ if (eval {require Crypt::PK::ECC}) {
                my $f_priv = Irssi::get_irssi_dir()."/$f_name.key";
                my $f_pub  = Irssi::get_irssi_dir()."/$f_name.pub";
                if (-e $f_priv) {
-                       $print->("SASL: refusing to overwrite '$f_priv'");
+                       $print->("SASL: refusing to overwrite '$f_priv'", MSGLEVEL_CLIENTERROR);
                        return;
                }
 
@@ -341,16 +344,13 @@ if (eval {require Crypt::PK::ECC}) {
                my $priv = $pk->export_key_pem("private");
                my $pub = encode_base64($pk->export_key_raw("public_compressed"), "");
 
-               my $cmdchar = substr(Irssi::settings_get_str("cmdchars"), 0, 1);
-               my $cmd = "msg NickServ SET PROPERTY pubkey $pub";
-
                if (open(my $fh, ">", $f_priv)) {
                        chmod(0600, $f_priv);
                        print $fh $priv;
                        close($fh);
                        $print->("SASL: wrote private key to '$f_priv'");
                } else {
-                       $print->("SASL: could not write '$f_priv': $!");
+                       $print->("SASL: could not write '$f_priv': $!", MSGLEVEL_CLIENTERROR);
                        return;
                }
 
@@ -358,28 +358,72 @@ if (eval {require Crypt::PK::ECC}) {
                        print $fh $pub."\n";
                        close($fh);
                } else {
-                       $print->("SASL: could not write '$f_pub': $!");
+                       $print->("SASL: could not write '$f_pub': $!", MSGLEVEL_CLIENTERROR);
                }
 
+               my $cmdchar = substr(Irssi::settings_get_str("cmdchars"), 0, 1);
+               my $cmd = "msg NickServ SET PROPERTY pubkey $pub";
+               # TODO: change to 'SET PUBKEY' when freenode gets support for that
+
                if ($server) {
                        $print->("SASL: updating your Irssi settings...");
                        $sasl_auth{$net}{user} //= $server->{nick};
                        $sasl_auth{$net}{password} = "$f_name.key";
                        $sasl_auth{$net}{mech} = $mech;
                        cmd_sasl_save(@_);
-
-                       $print->("SASL: submitting key to NickServ...");
+                       $print->("SASL: submitting pubkey to NickServ...");
                        $server->command($cmd);
                } else {
                        $print->("SASL: update your Irssi settings:");
                        $print->("%P".$cmdchar."sasl set $net <nick> $f_name.key $mech");
-
-                       $print->("SASL: submit your public key to $net:");
+                       $print->("SASL: submit your pubkey to $net:");
                        $print->("%P".$cmdchar.$cmd);
                }
        }
 
+       sub cmd_sasl_pubkey {
+               my ($data, $server, $witem) = @_;
+
+               my $arg = $server ? $server->{tag} : $data;
+
+               my $f;
+               if (!length $arg) {
+                       Irssi::print("SASL: please select a server or specify a keyfile path",
+                                               MSGLEVEL_CLIENTERROR);
+                       return;
+               } elsif ($arg =~ m![/.]!) {
+                       $f = $arg;
+               } else {
+                       if ($sasl_auth{$arg}{mech} eq $mech) {
+                               $f = $sasl_auth{$arg}{password};
+                       } else {
+                               $f = lc "sasl-ecdsa-$arg";
+                               $f =~ s![ /]+!_!g;
+                               $f = "$f.key";
+                       }
+               }
+
+               $f = irssi_abspath($f);
+               if (!-e $f) {
+                       Irssi::print("SASL: keyfile '$f' not found", MSGLEVEL_CLIENTERROR);
+                       return;
+               }
+
+               my $pk = eval {Crypt::PK::ECC->new($f)};
+               if ($@ || !$pk || !$pk->is_private) {
+                       Irssi::print("SASL: no private key in file '$f'", MSGLEVEL_CLIENTERROR);
+                       Irssi::print("(keys using named parameters or PKCS#8 are not yet supported)",
+                                               MSGLEVEL_CLIENTERROR);
+                       return;
+               }
+
+               my $pub = encode_base64($pk->export_key_raw("public_compressed"), "");
+               Irssi::print("SASL: loaded keyfile '$f'");
+               Irssi::print("SASL: your pubkey is $pub");
+       }
+
        Irssi::command_bind('sasl keygen', \&cmd_sasl_keygen);
+       Irssi::command_bind('sasl pubkey', \&cmd_sasl_pubkey);
 };
 
 cmd_sasl_load();
git clone https://git.99rst.org/PROJECT