in reply to Re: line by line Encryption fun with Crypt::CBC and Rijndael? File Ownership issues?
in thread line by line Encryption fun with Crypt::CBC and Rijndael? File Ownership issues?

If you want to use CBC, you should take care that all but the last block have a length that is a multiple of the key length

No. The blocks passed to crypt have no relation to cypher blocks. The extra bytes will be buffered until the next call to crypt.

The start/crypt/finish mode allows you encrypt and decrypt arbitrary segments of the message (file) at a time, as long as you process the entire message from the start.

In fact, encrypt and decrypt are just thin wrappers around start/crypt/finish.

To circumvent these problems the "Cypher Block Chaining" was invented. [...] Which means that for every block (except the first one) you not only need the key to encrypt it, but also the preceding block.

Right, although Crypt::CBC normally uses a special value for the first block too: the salt.
Crypt::CBC uses salting to ensure that every message is encrypted with a different key.
Crypt::CBC uses chaining to ensure that every block is encrypted with a different key.

So if you want to decrypt CBC data you need reverse the encryption process exactly.

Aye, and he doesn't do that. If he really did want to add a line at a time, he'd file would have to look like

length-of-encrypted-line, encrypted-line, length-of-encrypted-line, en +crypted-line, ...

Each line would be a message that would be encrypted, and then decrypted individually (using encrypt and decrypt).

  • Comment on Re^2: line by line Encryption fun with Crypt::CBC and Rijndael? File Ownership issues?
  • Select or Download Code

Replies are listed 'Best First'.
Re^3: line by line Encryption fun with Crypt::CBC and Rijndael? File Ownership issues? (code)
by ikegami (Patriarch) on Dec 05, 2007 at 18:25 UTC

    I wrote the code you need.

    Some utility functions

    sub read_bytes { my ($fh, $to_read) = @_; my $buf = ''; while ($to_read) { my $bytes_read = read($fh, $buf, $to_read, length($buf)); die("$!\n") if !defined($bytes_read); die("Unexpected end of file\n") if !$bytes_read; $to_read -= $bytes_read; } return $buf; } sub read_uint32 { my ($fh) = @_; return (unpack('N', read_bytes($fh, 4))); } sub read_str { my ($fh) = @_; my $length = read_uint32($fh); return read_bytes($fh, $length); } sub write_uint32 { my ($fh, $n) = @_; print $fh (pack('N', $n)); } sub write_str { my ($fh, $str) = @_; print $fh (pack('N', length($str)), $str); }

    The line-by-line encrypter:

    sub writeSubmissions { my ($cipher, $log_file, $str) = @_; open(my $FH_encrypted, '>>', $log_file) or die; binmode $FH_encrypted; flock $FH_encrypted, LOCK_EX; write_str($FH_encrypted, $cipher->encrypt($str)); } my $key_file = 'key_file.txt'; my $act_log_file = "pem.enc"; my $pem_log_file = "act_encrypted.enc"; my $my_key = `openssl enc -bf-cbc -d -in \Q$key_file\E -k encrypt`; my $cipher = Crypt::CBC->new({ key => $my_key, cipher => 'Rijndael' }) +; writeSubmissions($cipher, ..., ...);

    The line-by-line decrypter:

    my $key_file = 'key_file.txt'; my $encrypted = '...'; my $decrypted = '...'; my $my_key = `openssl enc -bf-cbc -d -in \Q$key_file\E -k encrypt`; my $cipher = Crypt::CBC->new({ key => $my_key, cipher => 'Rijndael' }) +; open(my $FH_decrypted, '>', $decrypted) or die("Unable to create decrypted file \"$decrypted\": $!\n"); binmode $FH_decrypted; flock $FH_decrypted, LOCK_EX; open(my $FH_encrypted, '<', $encrypted) or die("Unable to open encrypted file \"$encrypted\": $!\n"); binmode $FH_encrypted; flock $FH_encrypted, LOCK_SH; while (!eof($FH_encrypted)) { print $FH_decrypted $cipher->decrypt(read_str($FH_encrypted)); }

    Update: Fixed bugs mentioned in replies.
    Update: Tested (minus the openssl bit). Fixed other bugs.

      Many thanks.
      I'm off to test away...

      I learn more and more about less and less until eventually I know everything about nothing.

        Oops!
        while (!eof($FH_decrypted)) {
        should be
        while (!eof($FH_encrypted)) {
        (Fixed in original)

      I'm a little lost in
      open(my $FH_encrypted, '<', $encrypted") or die("Unable to open encrypted file \"$encrypted\": $!\n"); binmode $FH_crypted; flock $FH_crypted, LOCK_SH; while (!eof($FH_encrypted)) { print $FH_decrypted $cipher->decrypt(read_str($FH_crypted)); }
      the $FH_crypted is confusing me. Is that a third filehandle or should it be either _decrypted or _encrypted?

      I learn more and more about less and less until eventually I know everything about nothing.
        Sorry, copy and paste error due to the lack of consistency between the file handle names in your two programs.
      Trying to understand what is going on here:
      sub read_bytes { my ($fh, $to_read) = @_; $buf = ''; while ($to_read) { <b>my $bytes_read = read($FH_decrypted, $buf, length($buf));</b> die("$!\n") if !defined($bytes_read); die("Unexpected end of file\n") if !$bytes_read; $to_read -= $bytes_read; } return $buf; }

      Should $FH_decrypted be $fh instead? Otherwise the value in $fh being passed to the function is seemingly never used.
      Why is the value of $buf set to an empty string? Won't length($buf) be zero and therefore nothing would ever be read?


      I learn more and more about less and less until eventually I know everything about nothing.
        my $bytes_read = read($fh, $buf, $to_read, length($buf));

        Sorry, so many mistakes. I went and tested the code, made a couple more fixes. See the original post with the code.

      Hi, I am trying to use your code and I get the "Unexpected end of file" error while decrypting.

      The call to SUB read_uint32 works fine the first time and the value of $to_read = 4 in SUB read_bytes. Inside SUB read_bytes in the while ($to_read) loop, the value of $buf = 'Rand' (the first 4 characters in my encrypted file).

      Once SUB read_unit32 returns control to SUB read_str, the value of $length is set at some large number. I get an error during the call: read_bytes($fh, $length).

      It attempts to read $length the first time around, so say length = 825373492 was passed to read_bytes. Then, to_read = 825373492 The first iteration through the while loop decrements $to_read as below: WHILE to_read = 825370130

      However, in the next iteration I get the error.

      13:05:54 ERROR ***** 13:05:54 Unexpected end of file

      Could you please help or guide me in the right direction? I apologize if my elaborating the problem in this way causes problems. I wasn't sure on how else to do it. Thanks, Sam
        Are you sure the file was created using my write_str? The point of this thread was to create an appendable encrypted file format. I proposed such a file format, my encrypter produces it, and my decrypter decrypts it (and only it).
        >perl 700015.pl Hello World foo bar >debug data.enc -rcx CX 0040 : -d100 l40 0100 00 00 00 20 53 61 6C 74-65 64 5F 5F 90 66 5E D2 ... Salted__.f^. ^^^^^^^^^^^ 0110 88 43 CE 87 E6 FE 44 DA-2C E8 84 FF 2B 2B A1 84 .C....D.,...++.. 0120 FD BB 96 89 00 00 00 18-53 61 6C 74 65 64 5F 5F ........Salted__ ^^^^^^^^^^^ 0130 1F 12 77 C8 38 9A 4B 69-A2 33 51 9D 03 60 50 27 ..w.8.Ki.3Q..`P' -q

        You seem to be missing the higlighted parts.

        700015.pl:

        use strict; use warnings; use Crypt::CBC qw( ); my $qfn = 'data.enc'; sub read_bytes { my ($fh, $to_read) = @_; my $buf = ''; while ($to_read) { my $bytes_read = read($fh, $buf, $to_read, length($buf)); die("$!\n") if !defined($bytes_read); die("Unexpected end of file\n") if !$bytes_read; $to_read -= $bytes_read; } return $buf; } sub read_uint32 { my ($fh) = @_; return (unpack('N', read_bytes($fh, 4))); } sub read_str { my ($fh) = @_; my $length = read_uint32($fh); return read_bytes($fh, $length); } sub write_uint32 { my ($fh, $n) = @_; print $fh (pack('N', $n)); } sub write_str { my ($fh, $str) = @_; print $fh (pack('N', length($str)), $str); } { my $key = Crypt::CBC->random_bytes(8); my $cipher = Crypt::CBC->new({ key => $key, cipher => 'Blowfish' }) +; { open(my $fh, '>', $qfn) or die; binmode($fh); write_str($fh, $cipher->encrypt("Hello World")); write_str($fh, $cipher->encrypt("foo bar")); } { open(my $fh, '<', $qfn) or die; binmode($fh); while (!eof($fh)) { print($cipher->decrypt(read_str($fh)), "\n"); } } }

        (Blowfish was used since I'm having a linking problem with Rjindael. You should get similar results with both)