package PDF::Reuse::Scramble;

use 5.006;
use strict;
use warnings;
use integer;

require PDF::Reuse;

our $VERSION = '0.02';

sub new
{  my $class = shift;
   my $self  = {};
   bless $self, $class;
   my %param = @_;
   for (keys %param)
   {   my $key = lc($_);
       $self->{$key} = $param{$_}; 
   }
   my $user    = $self->{'user'}     || ' ';
   my $pass    = $self->{'password'} || ' ';
   my $seed    = $self->{'seed'}     || time;
   my $nouser  = $self->{'nouser'};
   my $nopass  = $self->{'nopassword'};
   my $title   = $self->{'title'} || 'Password';
   my $trans   = $self->{'transaction'};
  
   srand($seed);
   if (! defined $trans)
   {   $trans  = uc(sprintf("%x",rand(999999)));
       $trans .= sprintf("%x",rand(999999));
       $trans .= uc(sprintf("%x",rand(999999)));
       $trans .= sprintf("%x",rand(999999));
       $trans .= uc(sprintf("%x",rand(999999)));
   }
   if  (($nouser) && ($user eq ' '))
   {   $user = uc(sprintf("%x",rand(999999)));
       while (length($user) < 4)
       {   $user .= uc(sprintf("%x",rand(999999)));
       }
   } 
   if (($nopass) && ($pass eq ' '))
   {   $pass = sprintf("%x",rand(999999));
       while (length($pass) < 4)
       {   $pass .= sprintf("%x",rand(999999));
       }
   } 
   while ((length($user) < 4) || (length($pass) < 4))
   {   if (length($user) < 4)
       {  print "User     (at least 5 letters) : ";
          $user = <STDIN>;
          chomp($user);
       }
       if (length($pass) < 4)
       {  print "Password (at least 5 letters) : ";
          $pass = <STDIN>;
          chomp($pass);
       }
   } 
   my $rest = 0;
   my $fullKey = '';
   my $matchKey = '';
   my $num2 = 17;
   my $rest2 = 0;
   my $corr = ord(substr($pass, 0, 1)) + ord(substr($user, 0, 1));
   my $lr = length($trans) - 1;
   my $k = $corr % $lr;
   my $lu = length($user) - 1;
   my $j = $corr % $lu;
   $corr = ($corr + ord(substr($trans, $k, 1)) + $j + $k) % 37;
   for (my $i = 0; $i < length($pass); $i++)
   {  my $num1 = ord(substr($pass,$i,1)) + $corr;
      my $spar = $num1 % 53;
      $num1 += ($i < length($user)) ? ord(substr($user, $i, 1)) : $i ;
      my $num3 = ord(substr($trans, $k, 1)) % 53;
      $num1 += ($spar > $num3 ) ? $num3 : ord(substr($trans, $k, 1));
      $num2 += $num1 + $num3;
      $rest += $num1 % 77;
      $rest2 += $num2 % 109;
      $num1 *= 7;
      $num1 %= 46;
      $num2 *= 9;
      $num2 %= 46;
      $num1 *= 17;
      $num2 *= 13;
      $num1 %= 256;
      $num2 %= 256;        
      $fullKey .= sprintf("%x", $num1);
      $matchKey .= sprintf("%x", $num2);
      $num2 = $num1 % 98;
      $corr = ord(substr($pass, $i, 1)) + ord(substr($user, $j, 1))
               + ord(substr($trans, $k, 1)) + $i + $j + $k;
      $j = $corr % $lu;
      $k = $corr % $lr;
      $corr = $corr % 37;
  }
  $fullKey .= sprintf("%x", $rest);
  $matchKey .= sprintf("%x", $rest2);
  $fullKey = uc($fullKey);
  $matchKey = uc($matchKey);
  my $l = sprintf("%d",(length($fullKey) / 2));
  my $halfKey = substr($fullKey, 0, $l);
  my $jsUser = ($nouser) ? $user : ' ';
  my $jsPass = ($nopass) ? $pass : ' ';
  my $str = <<"EOF";
function authorize()
{ var chances = 0;
  var User = '$jsUser';
  var Pass = '$jsPass';
  scramble = '$trans';
  while (chances < 3)
  { if (User.length < 4)
       User = app.response('',"Userid",'', true);
    if (Pass.length < 4)
       Pass = app.response('','$title','', true);
    var rest = 0;
    fullKey = '';
    var matchKey = '';
    var num2 = 17;
    var rest2 = 0;
    var corr = Pass.charCodeAt(0) + User.charCodeAt(0);
    var sl = scramble.length - 1;
    var k = corr % sl;
    var ul = User.length - 1;
    var j = corr % ul;
    corr = (corr + scramble.charCodeAt(k) + j + k) % 37;
    for (var i = 0; i < Pass.length; i++)
    { var num1 = Pass.charCodeAt(i) + corr;
      var spar = num1 % 53;
      num1 += (i < User.length) ? User.charCodeAt(i) : i;
      var num3 = scramble.charCodeAt(k) % 53;
      num1 += (spar > num3 ) ? num3 : scramble.charCodeAt(k);
      num2 += num1 + num3;
      rest += num1 % 77;
      rest2 += num2 % 109;
      num1 *= 7;
      num1 %= 46;
      num2 *= 9;
      num2 %= 46;
      num1 *= 17;
      num2 *= 13;
      num1 %= 256;
      num2 %= 256;
      fullKey += '' + util.printf("%x", num1);
      matchKey += '' + util.printf("%x", num2);        
      num2 = num1 % 98;
      corr = Pass.charCodeAt(i) + User.charCodeAt(j)
           + scramble.charCodeAt(k) + i + j + k;
      j = corr % ul;
      k = corr % sl;
      corr = corr % 37;
   }
   fullKey += '' + util.printf("%x", rest);
   var l = util.printf("%d",(fullKey.length / 2));
   halfKey = fullKey.slice(0, l);
   matchKey += '' + util.printf("%x", rest2);
   if (matchKey == '$matchKey')
     chances = 7;
   else
   {  halfKey = '';
      fullKey = '';
   }     
   chances++;
   Pass = '$jsPass';
   User = '$jsUser';
  }
  if (chances < 7)
     app.alert('Authorization failed, encrypted data will not be shown');       
} 
function encrypt(word)
{ var key = fullKey;
  if ((! key) || (! word))
     return '';
  var j = Math.round(Math.random() * 8) + 1;
  var out = '%' + util.printf("%x", (j + 48));
  var sl = scramble.length - 1;
  var kl = key.length - 1;
  if ((kl) && (j > kl))
    j %= kl;
  var corr = key.charCodeAt(j);
  j = corr % kl;
  var k = corr % sl;
  corr = (corr + k + j) % 45;
  for (var i = 0; i < word.length; i++ )
  {  var num = word.charCodeAt(i) + corr;
     corr = key.charCodeAt(j) + scramble.charCodeAt(k) + i + j + k;
     num += corr % 23; 
     num += scramble.charCodeAt(k) % 19;
     out += '%' + util.printf("%x", num);
     if (j < 1)
       j = kl;
     else
       j--; 
     k = corr % sl;
     corr %= 45;
  }
  return out;
}
function decrypt(word)
{ var key = halfKey;
  var out = '';
  if ((! key) || (! word))
     return out;
  word = unescape(word);
  var j = word.charAt(0);
  word = word.substring(1);
  var sl = scramble.length - 1;
  var kl = key.length - 1;
  if ((kl) && (j > kl))
    j %= kl;
  var corr = key.charCodeAt(j);
  j = corr % kl;
  var k = corr % sl;
  corr = (corr + k + j) % 45;
  for (var i = 0; i < word.length; i++ )
  {  var num = word.charCodeAt(i) - corr;
     corr = key.charCodeAt(j) + scramble.charCodeAt(k) + i + j + k;
     num -= corr % 23; 
     num -= scramble.charCodeAt(k) % 19;
     out += '%' + util.printf("%x", num);
     if (j < 1)
       j = kl;
     else
       j--; 
     k = corr % sl;
     corr %= 45;
  }
  return unescape(out);
}

EOF
  $self->{fullKey} = $fullKey;
  $self->{halfKey} = $halfKey;
  $self->{transaction} = $trans;
  $self->{jsCode} = \$str; 
  return $self;
}

##############################################################
# To scramble and translate a string to a hex-encoded string 
##############################################################
sub encrypt
{  my $self = shift;
   my $word = shift;
   my $key  = shift || $self->{halfKey};
   my $scramble = $self->{transaction};
   return unless ($word);
 
   my $j = rand(8) + 1;
   my $out = '%' . sprintf("%x", ord($j));
   my $lr = length($scramble) - 1;
   my $lh = length($key ) - 1;
       
   if (($lh) && ($j > $lh))
   { $j = $j % $lh;
   }

   my $corr = ord(substr($key , $j, 1));
   $j =  $corr % $lh;
   my $k = $corr % $lr;
   $corr = ($corr + $k + $j) % 45;

   my $num;    
   for (my $i = 0; $i < length($word); $i++ )
   {  $num  = ord(substr($word, $i, 1)) + $corr;
      $corr = ord(substr($key, $j, 1)) + ord(substr($scramble, $k, 1)) 
            + $i + $j + $k;
      $num += $corr % 23; 
      $num += ord(substr($scramble, $k, 1)) % 19;
      $out .= '%' . sprintf("%x", $num);
      if ($j < 1)
      {  $j = $lh; }
      else
      {  $j--;} 
      $k = $corr % $lr;
      $corr = $corr % 45;
   }
   return uc($out);
}

sub decrypt
{  my $self = shift;
   my $word = shift;
   my $key  = $self->{key};
   my $scramble = $self->{transaction};
   my $out = '';
   return unless ($word);
   if ($word =~ m/^(%[0-9A-F]{2})+\&?$/gso)
   {  $word =~ s/%(..)/pack('C',hex($1))/eg;
   } 
   my $j = substr($word, 0, 1);
   $word = substr($word, 1);
   
   my $lr = length($scramble) - 1;
   my $lh = length($key ) - 1;
       
   if (($lh) && ($j > $lh))
   { $j = $j % $lh;
   }

   my $corr = ord(substr($key , $j, 1));
   $j =  $corr % $lh;
   my $k = $corr % $lr;
   $corr = ($corr + $k + $j) % 45;

   my $num;    
   for (my $i = 0; $i < length($word); $i++ )
   {  $num  = ord(substr($word, $i, 1)) - $corr;
      $corr = ord(substr($key, $j, 1)) + ord(substr($scramble, $k, 1)) 
            + $i + $j + $k;
      $num -= $corr % 23; 
      $num -= ord(substr($scramble, $k, 1)) % 19;
      $out .= chr($num);
      if ($j < 1)
      {  $j = $lh; }
      else
      {  $j--;} 
      $k = $corr % $lr;
      $corr = $corr % 45;
   }
   return $out;
}

sub getJsCode
{  my $self = shift;
   return ${$self->{jsCode}};
}
sub getHalfKey
{  my $self = shift;
   return $self->{halfKey};
}
sub getFullKey
{  my $self = shift;
   return $self->{fullKey};
}
sub getTransaction
{  my $self = shift;
   return $self->{transaction};
}
sub getKeys
{  my $self = shift;
   return ($self->{fullKey}, $self->{halfKey}, $self->{transaction});
}
sub initJsCode
{  my $self = shift;
   PDF::Reuse::prJs($self->getJsCode());
   PDF::Reuse::prInit("authorize();");
}

sub fieldValue
{  my $self = shift;
   my $field = shift;
   my $value = shift;
   $value = $self->encrypt($value);
   PDF::Reuse::prField($field, "js: decrypt('$value')");
}

sub decryptInit
{  my $class = shift;
   my $self  = {};
   bless $self, $class;
   my %param = @_;
   for (keys %param)
   {   my $key = lc($_);
       $self->{$key} = $param{$_}; 
   }
   return $self;
}

1;

__END__

=head1 NAME

PDF::Reuse::Scramble - Scramble data transfer between Perl - Acrobat JavaScript

=head1 SYNOPSIS

A little test where everything is done in Perl. In a real case JavaScript
should be involved.

   use PDF::Reuse::Scramble;
   use strict;

   #############################################################
   # As there are no userid or password as parameters to new,
   # the system will ask for those two strings. (Don't use 
   # characters from an extended character set. Your operating
   # system and Acrobat/Reader might have different opinion
   # about character and numeric order)
   #############################################################
 
   my $s = PDF::Reuse::Scramble->new();

   my ($longKey, $shortKey, $transaction) = $s->getKeys();

   my $codedString = $s->encrypt('This text will be used');
   print "$codedString\n";

   ####################################################################
   # encrypt uses the short key by default, so that one has to be used
   # here for decryption. (If it had been encrypted by JavaScript, on
   # the other hand, the long key would have been used)
   ####################################################################
   
   my $d = PDF::Reuse::Scramble->decryptInit(key         => $shortKey,
                                             transaction => $transaction);
   my $text = $d->decrypt($codedString);
   print "$text\n";


=head1 ABSTRACT

This module has subroutines in Perl and functions in Acrobat JavaScript to encrypt
and decrypt data transferred between Perl and a PDF-document and back to Perl. There
is also a subroutine/function in both languages to create the keys used for 
the scrambling.

=head1 DESCRIPTION

This is an experimental module. It should work for Acrobat Reader 5.0.5 or higher.
It makes it possible to use the Reader and also Acrobat in a restricted way,
so you can decide who can read the data you send out, and when you get a response,
you can know that the right person answered.

When you use this module to encrypt data for an interactive PDF-document,
the process looks a little like this:

Data to be inserted in a PDF-document is encrypted with a B<short key> and
a transaction code.

When the user opens the document for the first time, he has to be authorized. 
He will be prompted for his userid and password, if this dialog hasn't been
suppressed. Encryption keys and an internal match string are recreated.
If the new and old match strings are equal, there is a good chance that the new
keys also are correct, but there is no guarantee ! (The match strings are only
present to help the user avoid small spelling mistakes. Big errors might not be
detected.)

When data is sent back to the server, a B<long key> together with a transaction
code will be used both to encrypt and decrypt it.

=head1 Methods

=head2 new

    new( user        => $user,
         password    => $passWord, 
         seed        => $seed,
         noUser      => $nouser,
         noPassword  => $nopassword,
         title       => $title,
         transaction => $trans);
   
Creates a new instance of an encryption object.

All the parameters are optional.
If 'noUser' is set to something, Acrobat/Reader will NOT prompted for an userid when
the generated PDF-document is opened, and the system will generate an internal userid,
if it has not been given as the 'user' parameter. The same goes for 'noPassword'. 
The user will not be prompted for it, and an internal one will be generated if it is
not given by the 'password' parameter.

Both userid and password have to be at least 5 characters long.

Seed will be used for 'srand'. If it is not specified 'time' will be used.

Title is the title of the password dialog in the PDF-document.

Transaction is a key component in the scrambling.

=head2 encrypt 

    $encoded = $s->encrypt($stringToEncrypt [,$key])

returns an hex-encoded string which has been encrypted/scrambled

$key is optional. Don't use this parameter when you send encrypted data to 
a PDF-document. The JavaScripts use only the short key to decrypt and the long
key to encrypt.

=head2 fieldValue

    $s->fieldValue($fieldName, $stringToEncrypt);

Encrypts a string. Makes the JavaScript function decrypt to be called when the
PDF-document is opened, and makes the decrypted value to be assigned to the field
with the name $fieldName.

=head2 initJsCode
      
    $s->initJsCode();

Inserts a string with JavaScript code in the PDF-document you are creating.
It will be the JavaScript functions 'authorize', 'encrypt' and 'decrypt'.
'authorize' will be initiated to run when the document is opened the first time.

=head2 initDecrypt

    $d->initDecrypt(key         => $key,
                    transaction => $transactionCode);

Creates a new decryption object.

=head2 decrypt 

    $text = $d->decrypt($encodedString)

returns a decrypted string

=head1 EXAMPLES

=head2 A short example

You have a PDF-document with the interactive fields: field_1, field_2 and field_3 
(spelled exactly like that), which you want to fill with encrypted text. 
If you write the control code "AX225", you will be able to read the encrypted texts.

   use PDF::Reuse;
   use PDF::Reuse::Scramble;
   use strict;

   prFile('hidden1.pdf');
   prCompress(1);
   my $s = PDF::Reuse::Scramble->new( nouser => 1,
                                      password => 'AX225',
                                      title    => 'Control Code');
   $s->initJsCode();
   $s->fieldValue('field_1', 'This is the first secret');
   $s->fieldValue('field_2', 'This is the second secret');
   $s->fieldValue('field_3', 'This is the third secret');
   prDoc('old.pdf');
   prEnd();

=head2 A long example (more or less complete)

This example looks a little bit big, because it shows how different programs
interact. It is two Perl programs and also JavaScript embedded in a generated
PDF-document. And I have also tried to make it a 'complete example'. Cut and
paste, save it as files and try it. If you have a local web server, you could
run the programs at the directory where your server looks for CGI-programs.

First we need a JavaScript file which defines interactive fields and buttons
and assigns values to them ('fab.js'):

   function fab()   
   {  var param = fab.arguments;       // Here are the parameters
      var page  = param[0];            // The first 3 parameters 
      var x     = param[1];            // are not encrypted
      var y     = param[2];
      var l;     
      var d;
      var labelText = [ "Mr_Ms", "First_Name", "Surname",
                        "Address", "City", "Zip_Code", "Country",
                        "Phone", "Mobile_Phone", "E-mail",
                        "Company", "Order_1", "Order_2",
                        "Order_3" ];   
      var k = 3;
      for ( var i = 0; i < labelText.length; i++)
      {   l = x + 80;               // length of the label
          d = y - 15;               // depth / hight

          //
          // a label field is created
          //

          var fieldName = labelText[i] + "Label";
          var lf1       = this.addField(fieldName, "text", page, [x,y,l,d]);
          lf1.fillColor = color.white;
          lf1.textColor = color.black;
          lf1.readonly  = true;
          lf1.textSize  = 12;
          lf1.defaultValue = labelText[i];
          lf1.value     = labelText[i];
          lf1.display   = display.visible;

          //
          // a text field for the customer to fill-in his/her data is created 
          //
  
          x = l + 2;
          l = x + 200;
          var tf1         = this.addField(labelText[i], "text", page, [x,y,l,d]);
          tf1.fillColor   = ["RGB", 1, 1, 0.94];
          tf1.strokeColor = ["RGB", 0.7, 0.7, 0.6];
          tf1.textColor   = color.black;
          tf1.borderStyle = border.s;
          tf1.textSize    = 12;
          tf1.display     = display.visible;

          //
          // Here below encrypted parameters are handled
          // 

          if (param[k])
          {   tf1.value        = decrypt(param[k]);
              tf1.defaultValue = tf1.value;
          }
          x = x - 82    // move 82 pixels to the left
          if (i == 3)
          {  y = y - 90;}
          else
          {  y = y - 17;   // move 17 pixels down
          }
          k++;
      }

      //
      // The update button is created
      //

      y = y + 34;
      x = x + 310;
      l = x + 75;
      d = y - 30;
      var f = this.addField("ButUpdate","button", page , [x,y,l,d]);
      f.setAction("MouseUp", "sendUpdates()");
      f.userName = "Press here to send updated data from this form";
      f.buttonSetCaption("Update");
      f.borderStyle = border.b;
      f.fillColor   = ["RGB", 0.3, 0.7, 0.3];
      
      //
      // The mail button
      //

      x = x + 100;
      l = x + 75;
      var m = this.addField("ButMail","button", page , [x,y,l,d]);
      m.setAction("MouseUp", "mailUpdates()");
      m.userName = "Press here to send data from this form by e-mail";
      m.buttonSetCaption("Mail");
      m.borderStyle = border.b;
      m.fillColor   = ["RGB", 1, 0.8, 0.4];          
   }

   function sendUpdates()
   {  //
      // To be sure that the keys are defined
      //

      authorize();

      //
      // If the authorizing failed, nothing will be sent
      //

      if (! fullKey)
         return;      
      var str = 'r=' + scramble + '&' + 're=' + encrypt(scramble) + '&';
      for (var i = 0; i < this.numFields; i++)
      {   var theName = this.getNthFieldName(i);
          var f = this.getField(theName);
          if ((f.type == 'text') && (f.defaultValue != f.value))

          // 
          // Field values are encrypted
          //
 
          {   str = str + theName + '=' + encrypt(f.value) + '&';}
      }
      var dest = 'http://127.0.0.1:80/cgi-bin/update.pl?cust=' 
                                  + getCust() + '&' + str; 
      this.getURL(dest, false);
   }
   function mailUpdates()
   {  authorize();
      if (! fullKey)
         return;
      var str = 'cust=' + getCust() + '&';
      for (var i = 0; i < this.numFields; i++)
      {   var theName = this.getNthFieldName(i);
          var f = this.getField(theName);
          if ((f.type == 'text') && (f.defaultValue != f.value)) 
          {   str = str + theName + '=' + encrypt(f.value) + '&';}
      }
      app.mailMsg( {bUI: true, cTo: "com@company.com", 
                    cSubject: "This is the subject", cMsg: str} );
   }

Here is a Perl program which creates a PDF-document and uses 'fab.js'

     use PDF::Reuse;
     use PDF::Reuse::Scramble;
     use strict;
     
     #####################################################
     # Data about the customer, should be from a database
     #####################################################

     my $customerNo   = 5;  
     my @customerData = ('Mr', 'Peter', 'Johansson', 'Kungsgatan 9', 'Olovstad',
                         'SE-10010', 'Sweden', '+46119-23456', '+4670-777777',
                         'pj@com', 'Tot. Invented Inc.');

     my $userId       = '12Peter';         # Case sensitive !
     my $password     = 'Crazy Horse';     # Case sensitive !

     ##################################################
     # The document should be compressed so the keys 
     # and parameters are not directly visible (but 
     # it is not a big deal if they are seen)
     #################################################

     prFile('hidden.pdf');
     prCompress(1);

     #############################################################
     # Keys are calculated and JavaScript code is "generated"
     #############################################################

     my $s = PDF::Reuse::Scramble->new(user     => $userId,
                                       password => $password);

     #######################################################################
     # The JavaScript functions "authorize", "crypt" and "decrypt" will be 
     # inserted. "authorize" will run when the document is opened
     #######################################################################

     $s->initJsCode();

     ###############################################################
     # Here the full decryption key and transaction code are saved 
     # in a file instead of a database, just for this demonstration
     ###############################################################

     my $secrets = 'run.txt';
     my ($fullKey, $halfKey, $transaction) = $s->getKeys();
     open (OUTFILE, ">$secrets") || die $!;
     print OUTFILE "$fullKey\n";
     print OUTFILE "$transaction";
     close OUTFILE;

     ############################################
     # A function which always will give a fixed
     # value for this PDF-document
     ############################################

     prJs("function getCust() { return '$customerNo'; }");

     ########################################################
     # Data for the fill-in form is assigned and "encrypted"
     ########################################################

     my $parameters = "0, 100, 800";
     for (@customerData)
     {  $parameters .= ",'" . $s->encrypt($_) . "'";
     }
     ##########################################################
     # The JavaScript functions for the fill-in form is added 
     # and initiated
     ##########################################################

     prJs('fab.js');
     my $jsCode = "fab($parameters)";
     prInit($jsCode);

     ###########################################
     # An interactive field in the PDF-document  
     # will get an encrypted value 
     ###########################################

     $s->fieldValue('Order_2','Something really secret');

     prEnd();

And here at last is a little cgi-program 'update.pl' which receives encrypted
data from the PDF-document, decrypts it, and sends the result back.

   use PDF::Reuse;
   use PDF::Reuse::Scramble;
   use strict;

   my $x = 25;
   my $y = 790;
   my $step = 18;
   my ($string, %data, $value, $key);

   ###############################
   # First get data to work with 
   ###############################

   if ( $ENV{'REQUEST_METHOD'} eq "GET" 
   &&   $ENV{'QUERY_STRING'}   ne '') 
   {  $string = $ENV{'QUERY_STRING'};
   }

   ###############################################
   # Split and decode the hex-encoded strings
   # Create a hash with user data
   ###############################################
   for my $pair (split('&', $string)) 
   {  if ($pair =~ /(.*)=(.*)/)                     # found key=value;
      {   ($key,$value) = ($1,$2);                  # get key, value.
           $value =~ s/\+/ /g;
           $value =~ s/%(..)/pack('C',hex($1))/eg;  # Not really necessary here
           $data{$key} = $value;                    # Create the hash.
      }
   }

   #######################
   # Get the secret data
   #######################

   my $infile = 'run.txt';
   open (INFILE, "$infile");
   my $fullKey = <INFILE>;
   my $transaction  = <INFILE>;
   close INFILE;
   chomp($fullKey);

   #############################
   # Create a decryption object
   #############################

   my $d = PDF::Reuse::Scramble->decryptInit(key         => $fullKey,
                                             transaction => $transaction);

   #####################
   # Create new output
   #####################

   $| = 1;
   print STDOUT "Content-Type: application/pdf \n\n";

   prFile();
   prCompress(1);
   prTouchUp(0);
   prFontSize(16);

   #####################################################################
   # First a little check that the random string and the encrypted one
   # are equal. In a real situation, it is probably NOT a good idea
   # to put an encrypted and unencrypted value close to each other 
   #####################################################################

   if ((exists $data{'r'}) && (exists $data{'re'}))
   {  if ( $data{'r'} eq $d->decrypt($data{'re'}))
      {  prText($x, $y, "The messages are valid");
      }
      else
      {  prText($x, $y, "The messages are invalid");
      }
      $y -= $step * 3;
   }

   ##############################################
   # The transferred data is decrypted and shown
   ##############################################

   for $key (keys %data)
   {  if (($key eq 'cust') || ($key eq 'r'))
      {  prText($x, $y, "$key : $data{$key}");
      }
      else
      {  my $str = $d->decrypt($data{$key});
         prText($x, $y, "$key : $str");
      }
      $y -= $step;
      if ($y < 40)
      {  prPage();
         $y = 790;
      }
   }
   prEnd();

=head1 SEE ALSO

PDF::Reuse

PDF::Reuse::Tutorial

=head1 AUTHOR

Lars Lundberg, Solidez HB, elkelund@worldonline.se

=head1 COPYRIGHT AND LICENSE

Copyright 2003 by Lars Lundberg

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=head1 DISCLAIMER

I haven't worked very much with cryptography, so I am grateful for all suggestions
regarding this module.

You get this module free as it is, but nothing is guaranteed to work, whatever 
implicitly or explicitly stated in this document, and everything you do, 
you do at your own risk - I will not take responsibility 
for any damage, loss of money and/or health that may arise from the use of this document!
