#!/usr/bin/perl

# ($x, $p, $g, $y) = &generator_el_gamal( $p_lub_bity, $g )
#
# Oblicza nowa pare kluczy El Gamal poprzez losowe wybranie $x 
# i odpowiadajacej wartosci $y. Mozliwe jest podanie wartosci $p_lub_bity;
# mala liczba (mniejsza od 10000) bedzie decydowala o liczbie bitow $p,
# natomiast wieksza wartosc zostanie uzyta jako $p. Jesli nie podano
# zadnej wartosci $p_lub_bity, to wygenerowana liczba $p bedzie miala 512 bitow
# Jesli nie podano $g, to zostanie wygenerowana losowa wartosc mniejsza od $p.
    use SSLeay;
    my ( $p, $g ) = @_;

    unless ( $p > 10000 ) {
        $p = SSLeay::BN::generate_prime( $p || 512, 0 );
    }
    unless ( $g ) {
        $g = SSLeay::BN::rand( $p->num_bits - 1 );
    }
    my $x = SSLeay::BN::rand( $p->num_bits - 1 );
    my $y = $g->mod_exp( $x, $p );

    return ( $x, $p, $g, $y );
}

# ($a, $b ) = &el_gamal_szyfruj( $wiadomosc, $p, $g, $y )
#
# Szyfruje $wiadomosc przy uzyciu klucza publicznego ( $p, $g, $y ).
sub el_gamal_szyfruj {
    use SSLeay;
    my ( $wiad, $p, $g, $y ) = @_;

    my $k;
    do { $k = SSLeay::BN::rand( $p->num_bits - 1 );
    } until $k->gcd( $p - 1 ) == 1;

    my $a = $g->mod_exp( $k, $p );
    my $b = $y->mod_exp( $k, $p );
    $b = $b->mod_mul( $wiad, $p );

    return ( $a, $b );
}

# $wiadomosc = &el_gamal_odszyfruj( $a, $b, $p, $x ).
#
# Odszyfrowywuje pare liczb wiadomosci $a i $b przy uzyciu klucza prywatnego ( $p, $x ).
sub el_gamal_odszyfruj {
    use SSLeay;
    my ( $a, $b, $p, $x ) = @_;

    my $wiadomosc = $b->mod_mul(
        $a->mod_exp( $x, $p )->mod_inverse( $p ),
        $p );

    return $wiadomosc;
}

# ($a, $b ) = &el_gamal_sygnatura( $wiadomosc, $p, $g, $x )
#
# Podpisuje $wiadomosc kluczem prywatnym ( $p, $g, $x ).
sub el_gamal_sygnatura {
    use SSLeay;
    my ( $wiad, $p, $g, $x ) = @_;

    my $k;
    do { $k = SSLeay::BN::rand( $p->num_bits - 1 );
    } until $k->gcd( $p - 1 ) == 1;

    my $a = $g->mod_exp( $k, $p );
    my $b = $p - 1 + $wiad - $x->mod_mul( $a, $p-1 );
    $b = $b->mod_mul( $k->mod_inverse( $p-1 ), $p-1 );

    return ( $a, $b );
}

# $weryfikacja = &el_gamal_weryfikuj( $wiadomosc, $a, $b, $p, $g, $y )
#
# Uzywa klucza publicznego ( $p, $g, $y ) do sprawdzenia, czy $wiadomosc
# jest zgodna z par liczb $a i $b.
sub el_gamal_weryfikuj {
    use SSLeay;
    my ( $wiad, $a, $b, $p, $g, $y ) = @_;

    my $lhs = $a->mod_exp( $b, $p );
    $lhs = $y->mod_exp( $a, $p )->mod_mul( $lhs, $p );
    my $rhs = $g->mod_exp( $wiad, $p );

    return $lhs == $rhs;
}

