in reply to Re: Encrypting large files with Crypt::Blowfish
in thread Encrypting large files with Crypt::Blowfish

Two things:

  1. The OP isn't saying "only core modules"; rather only "standard" modules. That might be those that come with his distribution, but I bet it means only those available on CPAN.
  2. Crypt::Blowfish can only deal with 8-byte blocks. That means, you need code like this:
    my $cipher = Crypt::Blowfish->new($key); my $buffer; while( read($in, $buffer, 8) ) { print $out $cipher->encrypt($buffer); }

    The OP seems to be asking about dealing with the last few bytes of the file, since the Crypt::Blowfish module requires exactly 8 bytes to operate.

The answer, OP, is to pad the last chunk. Something like this:

my $cipher = Crypt::Blowfish->new($key); my $buffer; while( read($in, $buffer, 8) ) { if ( length($buffer) < 8 ) { for (1..8-length($buffer)) { $buffer.=chr(0) } } print $out $cipher->encrypt($buffer); }

However, the best way to take care of the issue is to do as others have suggested and use Crypt::CBC to handle the mechanics for you.

<-radiant.matrix->
A collection of thoughts and links from the minds of geeks
The Code that can be seen is not the true Code
"In any sufficiently large group of people, most are idiots" - Kaa's Law

Replies are listed 'Best First'.
Re^3: Encrypting large files with Crypt::Blowfish
by PeterE (Initiate) on Oct 25, 2005 at 15:05 UTC
    Thanks to anybody replyed to my question :)

    Indeed, i have to use core modules except the crypt::Blowfish.
    This is the only module additional installed on the machine.

    The way radiant.matrix mentioned works fine.

    Except one little problem:
    The decrypted file contains some characters additional to the original file: ^@
    I tried various ways to analyse the problem, but get only a change of the amount of ^@

    So please give me a hint: what am i doing wrong?

    crypting:

    use Crypt::Blowfish; my $key = 'ThisIsMyKey'; my $cipher = Crypt::Blowfish->new($key); my $buffer; my $file = 'TestFile.txt'; open(my $in, "<", $file) or die "Couldn't open '$file' for read: $!"; open(my $out, ">", "$file.crypt") or die "Couldn't open '$file.crypt' +for write: $!"; while( read($in, $buffer, 8) ) { if ( length($buffer) < 8 ) { for (1..8-length($buffer)) { $buffer.=chr(0); print length($buffer); } } print $out $cipher->encrypt($buffer); }
    decrypting:
    my $key = 'ThisIsMyKey'; my $cipher = Crypt::Blowfish->new($key); my $buffer; my $file = 'TestFile.txt'; open(my $in, "<", "$file.crypt") or die "Couldn't open '$file' for re +ad: $!"; open(my $out, ">", "$file.decrypt") or die "Couldn't open '$file.decry +pt' for write: $!"; while( read($in, $buffer, 8) ) { print $out $cipher->decrypt($buffer); }
    Thanks a lot, PeterE

      Even though you can't use Crypt::CBC, you can learn from it's documentation. From said document, the way to pad is one of these:

      standard: (default) Binary safe pads with the number of bytes that should be truncated. So, if blocksize is 8, then "0A0B0C" will be padded with "05", resultin +g in "0A0B0C0505050505". If the final block is a full block of 8 bytes, then a whole block of "0808080808080808" is appended. oneandzeroes: Binary safe pads with "80" followed by as many "00" necessary to fill the block. If the last block is a full block and blocksize is 8, a block of "8000000000000000" will be appended. null: text only pads with as many "00" necessary to fill the block. If the last block is a full block and blocksize is 8, a block of "0000000000000000" will be appended. space: text only same as "null", but with "20".

      You will, of course, need to choose a method, and then make sure to do the *inverse* after decryption. So lets say you choose the 'oneandzeroes' method:

      sub encrypt { my $FileHandle = shift; my $cipher = shift; my ($buffer, $cyphertext); while ( read($FileHandle, $buffer, 8) ) { if ( length($buffer) == 8 ) { $cyphertext .= $cipher->encrypt($b +uffer) } } # reading is done, now deal with padding of last block if ( length($buffer) < 8 ) { my $len = length($buffer); $buffer .= chr( 8 - $len ); for (2..$len) { $buffer.=chr(0) } } elsif ( length($buffer) == 8 ) { # we add a full padding block! $buffer = chr(8); for (2..8) { $buffer.=chr(0) } } else { warn 'We should never have a buffer bigger than 8!!!' } # now encrypt the final block $cyphertext .= $cipher->encrypt($buffer); return $cyphertext; } #___ sub decrypt { my $FileHandle = shift; my $cipher = shift; my ($buffer, $plaintext); while ( read($FileHandle, $buffer, 8) ) { $plaintext .= $cipher->decrypt($buffer); } # trim the padding my $last_chunk = substr($plaintext, -8, 8, ''); #removes last 8, to +o! # remove for a char followed by a string of chr(00) that's 1-7 long # this should be the padding of 'oneandzeroes' $last_chunk =~ s/(.)(\x00{1,7})/; # check that out to see if it went well warn "Trim went all strange!" if ord($1) != length($2); $plaintext.=$last_chunk; #put it back }

      You should probably do things like binmode the files and the like too. Remember, the above examples are just that: examples. You'll need to do more error-handling and test things out yourself.

      I am curious why you aren't being allowed to use Crypt::CBC for this, since its implementation is much superior to rolling your own, as it considers all kinds of things you might forget to. I strongly suggest you explain to whomever set that rule that making an exception for Crypt::CBC is a really good idea, as Crypt::CBC and Crypt::Blowfish are really a pair.

      <-radiant.matrix->
      A collection of thoughts and links from the minds of geeks
      The Code that can be seen is not the true Code
      "In any sufficiently large group of people, most are idiots" - Kaa's Law