#!/usr/bin/perl

use SSLeay;

# ukryj_wiad( $wiadomosc, $p, $g, $x, \&wyslij_wiad, $zestaw_znakow, $liczba )
#   Procedura wyslij_wiad sluzy do przeslania wielu fragmentow oryginalnej
#   wiadomosci oraz duzej liczby "smieci". Poprawne wartosci sa uwierzytelniane
#   poprzez wartosci $p, $g i $x klucza El Gamal. Ciag $zestaw_znakow
#   definiuje zestaw znakow dla losowych smieci i powinien zawierac
#   wszystkie znaki obecne w wiadomosci. Wartosc $liczba decyduje
#   o ilosci losowych smieci wysylanych dla kazdego bajta oryginalnej
#   wiadomosci (jest to srednia liczba kopii kazdej wartosci w $zestaw_znakow, 
#   jaka zostanie wyslana).
sub ukryj_wiad {
    my ($wiad, $p, $g, $x, $wyslij_wiad, $zestaw_znakow, $liczba) = @_;
    $zestaw_znakow = pack( "c256", 0..255 ) unless $zestaw_znakow;
    my $dlugosc = length( $zestaw_znakow );
    $liczba ||= 5;
    $liczba *= $dlugosc;

    my $sekw = 0;

    foreach $bajt ( split( //, $wiad ) ) {
        # Wstawienie wiadomosci w losowym miejscu wewnatrz smieci.
        my $poz = rand $liczba;
        foreach $proba ( 0 .. ($liczba-1) ) {
            my ( $m, $a, $b );
            if ( $proba == $poz ) {
                # Wyslanie prawdziwej wiadomosci.
                $m = sprintf( "%d:%02x", $sekw,
                                    substr( $wiad, $sekw, 1 ) );
                ( $a, $b ) =
                    el_gamal_podpisz( SSLeay::BN::bin2bn( $wiadomosc ) );
            } else {
                # Wygenerowanie oszukanej wiadomosci.
                $m = sprintf( "%d:%02x", $sekw,
                                   substr( $zestaw_znakow, rand($dlugosc), 1) );
                $a = SSLeay::BN::rand( $p->num_bits - 1 );
                $b = SSLeay::BN::rand( $p->num_bits - 1 );
            }
            &$wyslij_wiad( "$m:$a:$b\n" );
        }
        ++$sekw;
    }
}

# wyslij_wiadomosc( $wiadomosc )
#   Wysyla wiadomosc do odbiorcy.
sub wyslij_wiadomosc {
    my $wiadomosc = shift;

    open MAIL, "| mail $odbiorca";
    print MAIL "\n$wiadomosc";
    close MAIL;
}

$prawdziwa_wiadomosc = "To jest wiadomosc.\n";

ukryj_wiad( $prawdziwa_wiadomosc, $p, $g, $x, \&wyslij_wiadomosc );

# sprawdz_wiadomosc( $wiadomosc, $p, $g, $y, \&akceptacja_wiad )
#   sprawdzenie czy $wiadomosc jest poprawnie podpisana.
#   Jesli tak, to nastepuje akceptacja wartosci i polozenia.
sub sprawdz_wiadomosc {
    my ( $wiad, $p, $g, $y, $akceptacja_wiad ) = @_;

    if ( my ( $podpis, $sekw, $znak_szesn, $a, $b )
                = ($wiad =~ m/^((\d+):([\da-fA-F]{2})):(\d+):(\d+)$/) )
        {
          $a = SSLeay::BN::dec2bn( $a );
          $b = SSLeay::BN::dec2bn( $b );
          # Teraz dostepny jest klucz sekw:znak_szesn:a:b do weryfikacji.
          if ( el_gamal_weryfikuj( $podpis, $a, $b, $p, $g, $y ) ) {
              &$akceptacja_wiad( $sekw, pack( "H2", $znak_szesn ) );
          }
    }
}

# Brak argumentu oznacza uzycie aktualnego katalogu.
$katalog = shift || ".";

# Akceptacja listy plikow.
if ( -d $katalog ) {
    # Katalog zostaje przeksztalcony do listy plikow.
    unshift @ARGV, grep( -f, <$katalog/*> );
} else {
    # Lista plikow zostala dostarczona.
    unshift @ARGV, $katalog;
}

# Zaladowanie wartosci klucza publicznego z pliku:
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 $wiadomosc = "";

# Odnalezienie ziarna wsrod plew.
while ( <> ) {
    sprawdz_wiadomosc( $_, $p, $g, $y, \&moja_akceptacja );
}

print $wiadomosc;

sub moja_akceptacja {
    my ( $poz, $wartosc ) = @_;
    substr( $wiadomosc, $poz, 1 ) = $wartosc;
}

