## OpenCA::Token::LunaCA3.pm 
##
## Written by Michael Bell for the OpenCA project 2003
## Copyright (C) 2003-2004 The OpenCA Project
## All rights reserved.
##
##    This library is free software; you can redistribute it and/or
##    modify it under the terms of the GNU Lesser General Public
##    License as published by the Free Software Foundation; either
##    version 2.1 of the License, or (at your option) any later version.
##
##    This library is distributed in the hope that it will be useful,
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
##    Lesser General Public License for more details.
##
##    You should have received a copy of the GNU Lesser General Public
##    License along with this library; if not, write to the Free Software
##    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
##

use strict;

###############################################################
##        ============    LunaCA3 Token    =============     ##
###############################################################

## OpenCA::OpenSSL includes code to support the Chrysalis-ITS token too
## errorcodes 713*  71 -> token ; 3 -> third implemented token

package OpenCA::Token::LunaCA3;

use OpenCA::OpenSSL;

our ($errno, $errval, $AUTOLOAD, $STDERR);
$STDERR = \*STDERR;

($OpenCA::Token::LunaCA3::VERSION = '$Revision: 1.1.1.1 $' )=~ s/(?:^.*: (\d+))|(?:\s+\$$)/defined $1?"0\.9":""/eg;

# Preloaded methods go here.

## create a new LunaCA3 token
sub new {

   $ENV{'LD_LIBRARY_PATH'}=$ENV{'LD_LIBRARY_PATH'}.":/apps/usr/luna/lib";	
    my $that = shift;
    my $class = ref($that) || $that;

    my $self = {
                DEBUG     => 0,
                debug_fd  => $STDOUT,
                ## debug_msg => ()
               };

    bless $self, $class;

    my $keys = { @_ };
    $self->{DEBUG}     = $keys->{DEBUG};
    $self->{CRYPTO}    = $keys->{OPENCA_CRYPTO};
    $self->{gettext}   = $keys->{GETTEXT};
    $self->{NAME}      = $keys->{OPENCA_TOKEN};
    $self->{MODE}      = $keys->{TOKEN_MODE};
    $self->{UTILITY}   = $keys->{UTILITY};
    $self->{SLOT}      = $keys->{SLOT};
    $self->{APPID}     = $keys->{APPID};
    $self->{LOCK_FILE} = $keys->{LOCK_FILE};
    $self->{KEY}       = $keys->{KEY};
    return undef if (not $self->{CRYPTO});
    return undef if (not $self->{NAME});
    
    $keys->{ENGINE}  = "LunaCA3";

    if ($self->{MODE}=~ /^(SESSION|DAEMON)$/i)
    {
    	my $lower=1000;
    	my $upper=50000;
    	my $HiRandom = int(rand( $upper-$lower + 10000 ) ) + $lower;
    	my $LoRandom = int(rand ($upper -$lower + 1)) + $lower ;

    	my $AppID = "$HiRandom:$LoRandom";
#print "\n AppID = $HiRandom:$LoRandom\n";
	#$self->{APPID} = $AppID;
	$self->{APPID} = $keys->{APPID};;
	if (not $self->login()){
		$errno  = 7134014;
	        $errval = i18nGettext ("Cannot use the private key of the CA (__ERRNO__). __ERRVAL__",
                        "__ERRNO__", $self->errno(),
			"__ERRVAL__", $self->errval());
       		return undef;
    	}
	print "                 OK";
	$keys->{PRE_ENGINE} = " ENGINE_INIT:".   $self->{SLOT}.":".$self->{APPID};	

		
    }



    ## create openssl object
    # Chrysalis changed the OpenSSL patch to be more compliant with OpenSSL
    # we have no longer to send the slot and application ID
    #$keys->{ENGINE} = "LunaCA3 -enginearg ".
    #                  $self->{SLOT}.":".$self->{APPID};
    $keys->{KEYFORM} = "PEM";
    $self->debug ("initing OpenSSL");
    $self->{OPENSSL} = OpenCA::OpenSSL->new ( %{$keys} );
    $errno  = $OpenCA::OpenSSL::errno;
    $errval = $OpenCA::OpenSSL::errval;

    return undef if not $self->{OPENSSL};

    $self->debug ("module ready");
    return $self;
}

sub setError {
    my $self = shift;

    if (scalar (@_) == 4) {
        my $keys = { @_ };
        $errval = $keys->{ERRVAL};
        $errno  = $keys->{ERRNO};
    } else {
        $errno  = $_[0];
        $errval = $_[1];
    }
    return undef if (not $errno);

    print $STDERR "PKI Master Alert: OpenCA::Token::LunaCA3 error\n";
    print $STDERR "PKI Master Alert: Aborting all operations\n";
    print $STDERR "PKI Master Alert: Error:   $errno\n";
    print $STDERR "PKI Master Alert: Message: $errval\n";
    print $STDERR "PKI Master Alert: debugging messages of empty token follow\n";
    $self->{debug_fd} = $STDERR;
    $self->debug ();
    $self->{debug_fd} = $STDOUT;

    ## support for: return $self->setError (1234, "Something fails.") if (not $xyz);
    return undef;
}

## failover to default token OpenSSL which uses -engine
## see new to get an idea what is going on
sub AUTOLOAD {
    my $self = shift;

    $self->debug ("starting autoloader");
    if ($AUTOLOAD =~ /OpenCA::OpenSSL/)
    {
        $self->debug ("even openssl cannot handle this function");
        print STDERR "PKI Master Alert: OpenCA::OpenSSL is missing a function\n";
        print STDERR "PKI Master Alert: $AUTOLOAD\n";
#        $self->setError (666,
#            $self->{gettext} ("OpenCA::OpenSSL is missing a function. __FUNCTION__",
#                              "__FUNCTION__", $AUTOLOAD));
        return undef;
    }
    $self->debug ("OpenCA::Token::LunaCA3: AUTOLOAD => $AUTOLOAD");

    return 1 if ($AUTOLOAD eq 'OpenCA::Token::LunaCA3::DESTROY');

    $self->debug ("starting openssl");
    my $function = $AUTOLOAD;
    $function =~ s/.*:://g;
    my $ret = $self->{OPENSSL}->$function ( @_ );
    $self->setError ($OpenCA::OpenSSL::errno, $OpenCA::OpenSSL::errval);
    $self->debug ("openssl called");
    return $ret;
}

sub login {
    my $self = shift;
    $self->debug ("entering fucntion");

    my $keys = { @_ };

   if ( -e $self->{LOCK_FILE}) {
        return 1;
   }


    my $command = $self->{UTILITY};
    $command .= " -o ";
    $command .= " -s ".$self->{SLOT};
    $command .= " -i ".$self->{APPID};

	
    $self->debug ("executing login");
    my $ret = `$command`;
    if ($? != 0)
    {
        $self->setError ($?, $ret);
        $self->debug ("login failed");
        return undef;
    } else {
        $self->{ONLINE} = 1;
        $self->debug ("login succeeded");
	if ($self->{MODE} =~ /^(SESSION|DAEMON)$/i)
        {
            $self->debug ("touching session file");
            my $command = "touch ".$self->{LOCK_FILE};
            `$command`;
        }
        return 1;
    }
}

sub logout {
    my $self = shift;

    my $keys = { @_ };

    my $command = $self->{UTILITY};
    $command .= " -c ";
    $command .= " -s ".$self->{SLOT};
    $command .= " -i ".$self->{APPID};

    my $ret = `$command`;
    if ($? != 0)
    {
        $self->setError ($?, $ret);
        return undef;
    } else {
        $self->{ONLINE} = 0;
	unlink $self->{LOCK_FILE} if (-e $self->{LOCK_FILE});
        return 1;
    }
}

sub online {
    ## FIXME: how we can test a HSM to be online?
    ## FIXME: while we cannot test this we have no chance to
    ## FIXME: run this HSM in mode session or daemon
    my $self = shift;

    if ($self->{ONLINE} or -e $self->{LOCK_FILE}) {
        return 1;
    } else {
        return undef;
    }
}

sub keyOnline {
    my $self = shift;
    return $self->online;
}

sub getMode {
    my $self =  shift;
    return $self->{MODE};
}

sub genKey {
    my $self = shift;

    my $keys = { @_ };

    return $self->setError (7134012,
               $self->{gettext} ("You try to generate a key for a Chrysalis-ITS Luna CA3 token but you don't specify the number of bits."))
        if (not $keys->{BITS});

    return $self->setError (7134014,
               $self->{gettext} ("You try to generate a key for a Chrysalis-ITS Luna CA3 token but you don't specify the filename where to store the keyreference."))
        if (not $self->{KEY});
    my $command = $self->{UTILITY};
    $command .= " -s ".$self->{SLOT};
    $command .= " -i ".$self->{APPID};
    $command .= " -g ".$keys->{BITS};
    $command .= " -f ".$self->{KEY};
    my $ret = `$command`;
    if ($? != 0)
    {
        $self->setError ($?, $ret);
        return undef;
    } else {
        return 1;
    }
}

sub debug
{
    my $self     = shift;
    return 1 if (not $self->{DEBUG});
    my $msg      = shift;

    my ($package, $filename, $line, $subroutine, $hasargs,
        $wantarray, $evaltext, $is_require, $hints, $bitmask) = caller(0);
    $msg = "(line $line): $msg";

    ($package, $filename, $line, $subroutine, $hasargs,
     $wantarray, $evaltext, $is_require, $hints, $bitmask) = caller(1);
    $msg = "$subroutine $msg\n";

    print STDERR $msg;
}

1;
