#!/usr/bin/perl

use SSLeay;

# chaffmsg( $message, $p, $g, $x, \&sendmsg, $charset, $mult )
#   Use the sendmsg routine to transmit many messages that
#   together comprise the original message plus a huge volume
#   of chaff.  The valid parts have been authenticated with
#   the $p, $g, $x values of an El Gamal key.  The $charset
#   string specifies the set of characters from which to choose
#   random chaff - it should contain *all* characters that
#   might be in the message.  The $mult value specifies how
#   much chaff will be sent for each byte of the original
#   message (it specifies the average number of copies of every
#   value in $charset that will be sent).
sub chaffmsg {
    my ($msg, $p, $g, $x, $sendmsg, $charset, $mult) = @_;
    $charset = pack( "c256", 0..255 ) unless $charset;
    my $cslen = length( $charset );
    $mult ||= 5;
    $mult *= $cslen;

    my $seq = 0;

    foreach $byte ( split( //, $msg ) ) {
        # Insert the real message in a random position within the chaff.
        my $rpos = rand $mult;
        foreach $try ( 0 .. ($mult-1) ) {
            my ( $m, $a, $b );
            if ( $try == $rpos ) {
                # Time to send the real message.
                $m = sprintf( "%d:%02x", $seq,
                                    substr( $msg, $seq, 1 ) );
                ( $a, $b ) =
                    el_gamal_sign( SSLeay::BN::bin2bn( $message ) );
            } else {
                # Generate a fake message.
                $m = sprintf( "%d:%02x", $seq,
                                   substr( $charset, rand($cslen), 1) );
                $a = SSLeay::BN::rand( $p->num_bits - 1 );
                $b = SSLeay::BN::rand( $p->num_bits - 1 );
            }
            &$sendmsg( "$m:$a:$b\n" );
        }
        ++$seq;
    }
}

# mailmessage( $message )
#   Send the message to our friend.
sub mailmessage {
    my $message = shift;

    open MAIL, "| mail $friend";
    print MAIL "\n$message";
    close MAIL;
}

$realmessage = "This is a message.\n";

chaffmsg( $realmessage, $p, $g, $x, \&mailmessage );

# checkmessage( $message, $p, $g, $y, \&msgaccept )
#   test whether $message is properly signed.
#   if so, accept the value and position.
sub checkmessage {
    my ( $msg, $p, $g, $y, $msgaccept ) = @_;

    if ( my ( $signmsg, $seq, $hexchar, $a, $b )
                = ($msg =~ m/^((\d+):([\da-fA-F]{2})):(\d+):(\d+)$/) )
        {
          $a = SSLeay::BN::dec2bn( $a );
          $b = SSLeay::BN::dec2bn( $b );
          # It has seq:hexchar:a:b now to validate them.
          if ( el_gamal_valid( $signmsg, $a, $b, $p, $g, $y ) ) {
              &$msgaccept( $seq, pack( "H2", $hexchar ) );
          }
    }
}

# No argument means current directory.
$msgdir = shift || ".";

# Accept a list of files, too.
if ( -d $msgdir ) {
    # Explicit or implit directory - turn it into a list of files.
    unshift @ARGV, grep( -f, <$msgdir/*> );
} else {
    # List of files was provided.
    unshift @ARGV, $msgdir;
}

# Load our friend's public key values from a file:
open KEY, "</lib/friendkey";
$p = <KEY>;
$g = <KEY>;
$y = <KEY>;
close KEY;
$p = SSLeay::BN::dec2bn( $p );
$g = SSLeay::BN::dec2bn( $g );
$y = SSLeay::BN::dec2bn( $y );

my $themessage = "";

# Find the wheat amongst that chaff.
while ( <> ) {
    checkmessage( $_, $p, $g, $y, \&myaccept );
}

print $themessage;

sub myaccept {
    my ( $pos, $val ) = @_;
    substr( $themessage, $pos, 1 ) = $val;
}
