summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Hornburg (Racke) <racke@linuxia.de>2016-09-17 17:20:15 +0200
committerStefan Hornburg (Racke) <racke@linuxia.de>2016-09-17 17:20:15 +0200
commitf064ae60367c773a9a4a5dff8dec904c88274216 (patch)
treef6f37ec6d8e6ccbf3031fb89bdfa33660172ed3e
Import ciphersaber_1.01.orig.tar.gz
[dgit import orig ciphersaber_1.01.orig.tar.gz]
-rw-r--r--Build.PL23
-rw-r--r--Changes37
-rw-r--r--MANIFEST17
-rw-r--r--META.json47
-rw-r--r--META.yml25
-rw-r--r--Makefile.PL16
-rw-r--r--README34
-rw-r--r--lib/Crypt/CipherSaber.pm275
-rw-r--r--t/CS2.t19
-rw-r--r--t/base.t36
-rw-r--r--t/bigfile.t25
-rw-r--r--t/both_long.t20
-rw-r--r--t/create.t16
-rw-r--r--t/encrypt.t32
-rw-r--r--t/fh_encrypt.t120
-rw-r--r--t/smiles.cs1bin0 -> 1423 bytes
-rw-r--r--t/smiles.pngbin0 -> 1413 bytes
17 files changed, 742 insertions, 0 deletions
diff --git a/Build.PL b/Build.PL
new file mode 100644
index 0000000..0002cdd
--- /dev/null
+++ b/Build.PL
@@ -0,0 +1,23 @@
+use strict;
+use warnings;
+use Module::Build;
+
+my $builder = Module::Build->new(
+ module_name => 'Crypt::CipherSaber',
+ license => 'perl',
+ dist_author => 'chromatic <chromatic@cpan.org>',
+ dist_version_from => 'lib/Crypt/CipherSaber.pm',
+ requires =>
+ {
+ 'Scalar::Util' => '1.004002',
+ },
+ build_requires =>
+ {
+ 'Test::Simple' => '0.60',
+ 'Test::Warn' => '0.30',
+ },
+ add_to_cleanup => [ 'Crypt-CipherSaber-*' ],
+ create_makefile_pl => 'traditional',
+);
+
+$builder->create_build_script();
diff --git a/Changes b/Changes
new file mode 100644
index 0000000..2a28a2c
--- /dev/null
+++ b/Changes
@@ -0,0 +1,37 @@
+Revision history for Perl extension Crypt::CipherSaber.
+
+1.00 Tue Jul 12 23:45:00 UTC 2005
+ - migrated to Build.PL
+ - added POD, POD coverage, and signature tests
+ - generated traditional Makefile.PL
+ - bumped up version number
+ - ported tests to Test::Simple
+ - bumped up test coverage
+ - updated README
+
+0.61 Sat May 25 17:31:52 UTC 2002
+ - avoid uninitialized value warnings in fh_crypt()
+
+ Thu May 10 2001
+ - fixed _gen_iv() to generate 255 characters (thanks to John Wiersba)
+
+ Sun Apr 29 2001
+ - added license/copyright information to the pod in the module itself
+
+0.60 Sat Apr 7 2001
+ - some internal cleanup
+ - added some comments
+ - added documentation for fh_crypt()
+
+ Thu Apr 5 2001
+ - made _gen_iv() work with Perl 5.004 (again Sympa)
+ - added fh_crypt()
+ - added _do_crypt() to help with fh_crypt()
+ - made crypt() use _do_crypt()
+ - added t/bigfile.t and t/fh_encrypt.t
+
+0.51 Wed Apr 4 2001 UNRELEASED VERSION
+ - made decrypt() work with Perl 5.004 (for Sympa project)
+
+0.50 Fri Dec 1 17:02:02 2000
+ - original version; created by h2xs 1.19
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..e890589
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,17 @@
+Build.PL
+Changes
+lib/Crypt/CipherSaber.pm
+Makefile.PL
+MANIFEST
+META.yml
+README
+t/CS2.t
+t/base.t
+t/bigfile.t
+t/both_long.t
+t/create.t
+t/encrypt.t
+t/fh_encrypt.t
+t/smiles.cs1
+t/smiles.png
+META.json
diff --git a/META.json b/META.json
new file mode 100644
index 0000000..71ccf9e
--- /dev/null
+++ b/META.json
@@ -0,0 +1,47 @@
+{
+ "abstract" : "Perl module implementing CipherSaber encryption.",
+ "author" : [
+ "chromatic <chromatic@cpan.org>"
+ ],
+ "dynamic_config" : 1,
+ "generated_by" : "Module::Build version 0.4211",
+ "license" : [
+ "perl_5"
+ ],
+ "meta-spec" : {
+ "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
+ "version" : "2"
+ },
+ "name" : "Crypt-CipherSaber",
+ "prereqs" : {
+ "build" : {
+ "requires" : {
+ "Test::Simple" : "0.60",
+ "Test::Warn" : "0.30"
+ }
+ },
+ "configure" : {
+ "requires" : {
+ "Module::Build" : "0.42"
+ }
+ },
+ "runtime" : {
+ "requires" : {
+ "Scalar::Util" : "1.004002"
+ }
+ }
+ },
+ "provides" : {
+ "Crypt::CipherSaber" : {
+ "file" : "lib/Crypt/CipherSaber.pm",
+ "version" : "1.01"
+ }
+ },
+ "release_status" : "stable",
+ "resources" : {
+ "license" : [
+ "http://dev.perl.org/licenses/"
+ ]
+ },
+ "version" : "1.01"
+}
diff --git a/META.yml b/META.yml
new file mode 100644
index 0000000..932da12
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,25 @@
+---
+abstract: 'Perl module implementing CipherSaber encryption.'
+author:
+ - 'chromatic <chromatic@cpan.org>'
+build_requires:
+ Test::Simple: '0.60'
+ Test::Warn: '0.30'
+configure_requires:
+ Module::Build: '0.42'
+dynamic_config: 1
+generated_by: 'Module::Build version 0.4211, CPAN::Meta::Converter version 2.150001'
+license: perl
+meta-spec:
+ url: http://module-build.sourceforge.net/META-spec-v1.4.html
+ version: '1.4'
+name: Crypt-CipherSaber
+provides:
+ Crypt::CipherSaber:
+ file: lib/Crypt/CipherSaber.pm
+ version: '1.01'
+requires:
+ Scalar::Util: '1.004002'
+resources:
+ license: http://dev.perl.org/licenses/
+version: '1.01'
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..6460abd
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,16 @@
+# Note: this file was auto-generated by Module::Build::Compat version 0.4211
+use ExtUtils::MakeMaker;
+WriteMakefile
+(
+ 'NAME' => 'Crypt::CipherSaber',
+ 'VERSION_FROM' => 'lib/Crypt/CipherSaber.pm',
+ 'PREREQ_PM' => {
+ 'Scalar::Util' => '1.004002',
+ 'Test::Simple' => '0.60',
+ 'Test::Warn' => '0.30'
+ },
+ 'INSTALLDIRS' => 'site',
+ 'EXE_FILES' => [],
+ 'PL_FILES' => {}
+)
+;
diff --git a/README b/README
new file mode 100644
index 0000000..66af40f
--- /dev/null
+++ b/README
@@ -0,0 +1,34 @@
+Crypt::CipherSaber
+------------------
+
+version 1.00 Wed Jul 13 01:37:53 UTC 2005
+
+Crypt::CipherSaber is a Perl module providing an object oriented interface to
+CipherSaber-1 and CipherSaber-2 encryption. See the POD for further details on
+use. See http://ciphersaber.gurus.com for more information about CipherSaber.
+
+After unpacking the tarball, to install this module type:
+
+ $ perl Build.PL
+ $ ./Build
+ $ ./Build test
+ # ./Build install
+
+You can also use the Makefile.PL, but I don't.
+
+The encryption itself is simple, relatively fast, and fairly secure. It uses a
+shared secret system, and is suitable even for binary files. It should run
+without modification anywhere Perl runs. This ought to work as far back as
+Perl 5.004, so please let me know how it does.
+
+Prerequisites: Scalar::Util
+Test requirements: Test::More, Test::Warn
+
+Potential enhancements and plans:
+
+ * built-in support for a better randomization scheme?
+ * support changing keys in an object rather than creating a new one?
+
+Copyright (c) 2001, 2005 chromatic (chromatic at wgz dot org), all rights
+reserved. This program is free software; you can use, modify, and redistribute
+it under the same terms as Perl 5.8.x itself.
diff --git a/lib/Crypt/CipherSaber.pm b/lib/Crypt/CipherSaber.pm
new file mode 100644
index 0000000..7cb7cc0
--- /dev/null
+++ b/lib/Crypt/CipherSaber.pm
@@ -0,0 +1,275 @@
+package Crypt::CipherSaber;
+
+use strict;
+
+use Carp;
+use Scalar::Util 'reftype';
+
+use vars '$VERSION';
+
+$VERSION = '1.01';
+
+sub new
+{
+ my ($class, $key, $N) = @_;
+
+ # CS-2 shuffles the state array N times, CS-1 once
+ if ( !( defined $N ) or ( $N < 1 ) )
+ {
+ $N = 1;
+ }
+ bless [ $key, [ 0 .. 255 ], $N ], $class;
+}
+
+sub crypt
+{
+ my ($self, $iv, $message) = @_;
+ $self->_setup_key($iv);
+
+ my $state = $self->[1];
+ my $output = _do_crypt( $state, $message );
+ $self->[1] = [ 0 .. 255 ];
+ return $output;
+}
+
+sub encrypt
+{
+ my $self = shift;
+ my $iv = $self->_gen_iv();
+ return $iv . $self->crypt( $iv, @_ );
+}
+
+sub decrypt
+{
+ my $self = shift;
+ my ( $iv, $message ) = unpack( "a10a*", +shift );
+ return $self->crypt( $iv, $message );
+}
+
+sub fh_crypt
+{
+ my ( $self, $in, $out, $iv ) = @_;
+
+ for my $glob ($in, $out)
+ {
+ my $reftype = reftype( $glob ) || '';
+ unless ($reftype eq 'GLOB')
+ {
+ require Carp;
+ Carp::carp( 'Non-filehandle passed to fh_crypt()' );
+ return;
+ }
+ }
+
+ local *OUT = $out;
+ if ( defined($iv) )
+ {
+ $iv = $self->_gen_iv() if length($iv) == 1;
+ $self->_setup_key($iv);
+ print OUT $iv;
+ }
+
+ my $state = $self->[1];
+
+ my ( $buf, @vars );
+
+ while (<$in>)
+ {
+ unless ($iv)
+ {
+ ( $iv, $_ ) = unpack( "a10a*", $_ );
+ $self->_setup_key($iv);
+ }
+ my $line;
+ ( $line, $state, @vars ) = _do_crypt( $state, $_, @vars );
+ print OUT $line;
+ }
+ $self->[1] = [ 0 .. 255 ];
+ return 1;
+}
+
+###################
+#
+# PRIVATE METHODS
+#
+###################
+sub _gen_iv
+{
+ my $iv;
+ for ( 1 .. 10 )
+ {
+ $iv .= chr( int( rand(256) ) );
+ }
+ return $iv;
+}
+
+sub _setup_key
+{
+ my $self = shift;
+ my $key = $self->[0] . shift;
+ my @key = map { ord } split( //, $key );
+ my $state = $self->[1];
+ my $j = 0;
+ my $length = @key;
+
+ # repeat N times, for CS-2
+ for ( 1 .. $self->[2] )
+ {
+ for my $i ( 0 .. 255 )
+ {
+ $j += ( $state->[$i] + ( $key[ $i % $length ] ) );
+ $j %= 256;
+ ( @$state[ $i, $j ] ) = ( @$state[ $j, $i ] );
+ }
+ }
+}
+
+sub _do_crypt
+{
+ my ( $state, $message, $i, $j, $n ) = @_;
+
+ my $output = '';
+
+ for ( 0 .. ( length($message) - 1 ) )
+ {
+ $i++;
+ $i %= 256;
+ $j += $state->[$i];
+ $j %= 256;
+ @$state[ $i, $j ] = @$state[ $j, $i ];
+ $n = $state->[$i] + $state->[$j];
+ $n %= 256;
+ $output .= chr( $state->[$n] ^ ord( substr( $message, $_, 1 ) ) );
+ }
+
+ return wantarray ? ( $output, $state, $i, $j, $n ) : $output;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Crypt::CipherSaber - Perl module implementing CipherSaber encryption.
+
+=head1 SYNOPSIS
+
+ use Crypt::CipherSaber;
+ my $cs = Crypt::CipherSaber->new('my sad secret key');
+
+ my $coded = $cs->encrypt('Here is a secret message for you');
+ my $decoded = $cs->decrypt($coded);
+
+ # encrypt from and to a file
+ open my $in, 'secretletter.txt' or die "Can't open infile: $!";
+ open my $out, '>', 'secretletter.cs1' or die "Can't open outfile: $!";
+ binmode $in;
+ binmode $out;
+
+ $cs->fh_crypt($in, $out, 1);
+
+ # decrypt from and to a file
+ open my $in, 'secretletter.txt' or die "Can't open infile: $!";
+ open my $out, '>', 'secretletter.cs1' or die "Can't open outfile: $!";
+
+ binmode $in;
+ binmode $out;
+ $cs->fh_crypt($in, $out);
+
+=head1 DESCRIPTION
+
+The Crypt::CipherSaber module implements CipherSaber encryption, described at
+L<http://ciphersaber.gurus.com/>. It is simple, fairly speedy, and relatively
+secure algorithm based on RC4. I<Relatively>, given RC4.
+
+Encryption and decryption are done based on a secret key, which must be shared
+with all intended recipients of a message.
+
+=head1 METHODS
+
+=over
+
+=item B<new($key, $N)>
+
+Initialize a new Crypt::CipherSaber object. C<$key> is a required parameter:
+the key used to encrypt or to decrypt messages. C<$N> is optional. If
+provided and greater than one, it causes the object to use CipherSaber-2
+encryption (slightly slower but more secure). If not specified, or equal to 1,
+the module defaults to CipherSaber-1 encryption. C<$N> must be a positive
+integer greater than one.
+
+=item B<encrypt($message)>
+
+Encrypt a message. This uses the key stored in the current Crypt::CipherSaber
+object. It generates a 10-byte random IV (Initialization Vector)
+automatically, as defined in the RC4 specification. This returns a string
+containing the encrypted message.
+
+Note that the encrypted message may contain unprintable characters, as it uses
+the extended ASCII character set (valid numbers 0 through 255).
+
+=item B<decrypt($message)>
+
+Decrypt a message. For the curious, the first ten bytes of an encrypted
+message are the IV, so this must strip it off first. This returns a string
+containing the decrypted message.
+
+The decrypted message may also contain unprintable characters, as the
+CipherSaber encryption scheme handles binary filesIf this is important to you,
+be sure to treat the results correctly.
+
+=item B<crypt($iv, $message)>
+
+If you wish to generate the IV with a more cryptographically secure random
+string (at least compared to Perl's builtin C<rand()> operator), you may do so
+separately, passing it to this method directly. The IV must be a ten-byte
+string consisting of characters from the extended ASCII set.
+
+This is generally only useful for encryption, although you may extract the
+first ten characters of an encrypted message and pass them in yourself. You
+might as well call B<decrypt()>, though. The more random the IV, the stronger
+the encryption tends to be. On some operating systems, you can read from
+F</dev/random>. Other approaches are the L<Math::TrulyRandom> module, or
+compressing a file, removing the headers, and compressing it again.
+
+=item B<fh_crypt( $in_fh, $out_fh, ($iv))>
+
+For the sake of efficiency, Crypt::CipherSaber can operate on filehandles.
+It's not super brilliant, but it's relatively fast and sane. If your platform
+needs to use C<binmode()>, this is your responsibility. It is also your
+responsibility to close the files.
+
+You may also pass in an optional third parameter, an IV. There are three
+possibilities here. If you pass no IV, C<fh_crypt()> will pull the first ten
+bytes from the input filehandle and use that as an IV. This corresponds to
+decryption. If you pass in an IV of your own, it will use that when encrypting
+the file. If you pass in the value C<1>, it will generate a new, random IV for
+you. This corresponds to an encryption.
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2000 - 2015 chromatic
+
+This library is free software; you can use, modify, and redistribute it under
+the same terms as Perl 5.20.x itself.
+
+=head1 AUTHOR
+
+chromatic C<< chromatic at cpan dot org >>
+
+thanks to jlp for testing, moral support, and never fearing the icky details
+and to the fine folks at PerlMonks L<http://perlmonks.org/>.
+
+Additional thanks to Olivier Salaun and the Sympa project
+L<http://www.sympa.org> for testing.
+
+=head1 SEE ALSO
+
+the CipherSaber home page at L<http://ciphersaber.gurus.com/>
+
+perl(1), rand().
+
+=cut
diff --git a/t/CS2.t b/t/CS2.t
new file mode 100644
index 0000000..5c5367a
--- /dev/null
+++ b/t/CS2.t
@@ -0,0 +1,19 @@
+#!perl -w
+
+# now do a bidirectional check with CS-2
+
+BEGIN
+{
+ chdir 't' if -d 't';
+}
+
+use strict;
+use Test::More tests => 2;
+
+use_ok('Crypt::CipherSaber');
+
+my $cs2 = Crypt::CipherSaber->new( 'second key', 5 );
+my $long_line = join( ' ', ( 1 .. 100 ) );
+my $coded = $cs2->encrypt($long_line);
+is( $cs2->decrypt($coded), $long_line,
+ 'CS-2 should work on texts longer than IV' );
diff --git a/t/base.t b/t/base.t
new file mode 100644
index 0000000..a1ff1fd
--- /dev/null
+++ b/t/base.t
@@ -0,0 +1,36 @@
+#!perl -w
+
+BEGIN
+{
+ chdir 't' if -d 't';
+}
+
+use strict;
+
+use Test::More 'no_plan'; # tests => 2;
+use Test::Warn;
+
+my $module = 'Crypt::CipherSaber';
+use_ok( $module );
+
+# first, try to create an object
+my $cs = $module->new('first key');
+isa_ok( $cs, $module );
+is( $cs->[2], 1, 'new() should default to an N of 1 with none given' );
+
+$cs = $module->new('first key', 0);
+is( $cs->[2], 1, '... or one given < 1' );
+
+can_ok( $cs, 'crypt' );
+can_ok( $cs, 'encrypt' );
+can_ok( $cs, 'decrypt' );
+
+can_ok( $cs, 'fh_crypt' );
+my $result;
+warning_like { $result = $cs->fh_crypt() } qr/Non-filehandle/,
+ 'fh_crypt() should warn without a valid input filehandle';
+is( $result, undef, '... returning nothing' );
+
+warning_like { $result = $cs->fh_crypt( \*STDIN ) } qr/Non-filehandle/,
+ '... and should warn without a valid output filehandle';
+is( $result, undef, '... also returning nothing' );
diff --git a/t/bigfile.t b/t/bigfile.t
new file mode 100644
index 0000000..04c74f2
--- /dev/null
+++ b/t/bigfile.t
@@ -0,0 +1,25 @@
+#!perl -w
+
+BEGIN
+{
+ chdir 't' if -d 't';
+}
+
+use strict;
+use Test::More tests => 2;
+
+use_ok( 'Crypt::CipherSaber' );
+
+my $cs = Crypt::CipherSaber->new( 'sdrawkcabsihtdaeR' );
+open( INPUT, 'smiles.cs1' ) or die "Couldn't open: $!";
+binmode(INPUT);
+open(OUTPUT, '> smiles.png') or die "Couldn't open: $!";
+binmode(OUTPUT);
+$cs->fh_crypt(\*INPUT, \*OUTPUT);
+close INPUT;
+close OUTPUT;
+
+open(TEST, 'smiles.png') or die "Couldn't open: $!";
+my $line = <TEST>;
+
+like( $line, qr/PNG/, 'Encrypting a large file should not mangle it' );
diff --git a/t/both_long.t b/t/both_long.t
new file mode 100644
index 0000000..8055692
--- /dev/null
+++ b/t/both_long.t
@@ -0,0 +1,20 @@
+#!perl -w
+
+# encrypt and decrypt a line greater than 256 characters long
+# this tests for a subtle bug, ie, missing a modulo on $i
+
+BEGIN
+{
+ chdir 't' if -d 't';
+}
+
+use strict;
+
+use Test::More tests => 2;
+
+use_ok('Crypt::CipherSaber');
+
+my $cs = Crypt::CipherSaber->new( 'first key' );
+my $long_line = join( ' ', ( 1 .. 100 ) );
+my $coded = $cs->encrypt($long_line);
+is( $cs->decrypt( $coded ), $long_line, 'round-tripping should work' );
diff --git a/t/create.t b/t/create.t
new file mode 100644
index 0000000..013b4ff
--- /dev/null
+++ b/t/create.t
@@ -0,0 +1,16 @@
+#!perl -w
+
+BEGIN
+{
+ chdir 't' if -d 't';
+}
+
+use strict;
+use Test::More tests => 2;
+
+my $module = 'Crypt::CipherSaber';
+use_ok( $module );
+
+# first, try to create an object
+my $cs = $module->new('first key');
+isa_ok( $cs, $module );
diff --git a/t/encrypt.t b/t/encrypt.t
new file mode 100644
index 0000000..6b5e442
--- /dev/null
+++ b/t/encrypt.t
@@ -0,0 +1,32 @@
+#!perl -w
+
+BEGIN
+{
+ chdir 't' if -d 't';
+}
+
+use strict;
+
+use Test::More tests => 3;
+use_ok( 'Crypt::CipherSaber' );
+
+# encrypt a message
+my $cs = Crypt::CipherSaber->new( 'asdfg' );
+my $coded = $cs->crypt( 'abcdefghij', 'This is another test.' );
+my $message = join( '',
+ map { chr }
+ qw ( 153 90 51 37 126 114 217 0 50 245 103 36 219 18 4 44 169 53 32 64 15 )
+);
+
+is( $coded, $message, 'encryption of known text should produce known output' );
+
+# decrypt a previously encrypted message
+$message = join( '',
+ map { chr }
+ qw( 99 228 225 111 163 246 142 168 143 125 239 199 167 58 192 81 211 122 19
+ 200 97 57 101 151 19
+ )
+);
+
+is( $cs->decrypt($message), 'This is a test.',
+ '... and decryption should produce known output' );
diff --git a/t/fh_encrypt.t b/t/fh_encrypt.t
new file mode 100644
index 0000000..35a74fb
--- /dev/null
+++ b/t/fh_encrypt.t
@@ -0,0 +1,120 @@
+#!perl -w
+
+BEGIN
+{
+ chdir 't' if -d 't';
+}
+
+use strict;
+use Test::More tests => 6;
+use_ok( 'Crypt::CipherSaber' );
+
+# tests the fh_crypt() method
+# this will fail if the state array is not reinitialized ... oops!
+use Crypt::CipherSaber;
+use File::Spec;
+
+open( IN, 'smiles.cs1' ) or die "Can't get IV!\n";
+binmode(IN);
+my $iv = unpack( "a10", <IN> );
+
+# encrypt a message
+my $cs = Crypt::CipherSaber->new( 'sdrawkcabsihtdaeR' );
+open( IN, 'smiles.png' ) or die "Can't open input file!\n";
+open( OUT, '> outsmiles.cs1' ) or die "Can't open output file!\n";
+binmode(IN);
+binmode(OUT);
+
+ok( $cs->fh_crypt( \*IN, \*OUT, $iv ),
+ 'fh_crypt() should return true if everything works' );
+
+open( ENCRYPTED, 'outsmiles.cs1' ) or die "Can't open encrypted file!\n";
+open( FIXED, 'smiles.cs1' ) or die "Can't open fixed file!\n";
+binmode(ENCRYPTED);
+binmode(FIXED);
+
+my $status = 0;
+while (<ENCRYPTED>)
+{
+ my $fixed = <FIXED>;
+ my $pos = 0;
+ for my $char ( split( //, $_ ) )
+ {
+ next if ( substr( $fixed, $pos++, 1 ) eq $char );
+ $status = 1;
+ }
+ last if $status;
+}
+
+ok( ! $status,
+ 'There should be no characters in common between input and output' );
+
+open( IN, 'smiles.cs1' ) or die "Can't open input file 2!\n";
+open( OUT, '> outsmiles.png' ) or die "Can't open output file 2!\n";
+binmode(IN);
+binmode(OUT);
+
+ok( $cs->fh_crypt( \*IN, \*OUT ),
+ 'fh_crypt() should return true if everything works (decrypting)' );
+
+close(IN);
+close(OUT);
+
+open( ENCRYPTED, 'outsmiles.png' ) or die "Can't open encrypted file!\n";
+open( FIXED, 'smiles.png' ) or die "Can't open fixed file!\n";
+binmode(ENCRYPTED);
+binmode(FIXED);
+
+$status = 0;
+while (<ENCRYPTED>)
+{
+ my $fixed = <FIXED>;
+ unless ($_ eq $fixed)
+ {
+ $status = 1;
+ last;
+ }
+}
+
+close(ENCRYPTED);
+close(FIXED);
+
+ok( ! $status, '... with no characters in common' );
+open( IN, 'smiles.png' ) or die "Cannot read smiles.png: $!";
+open( OUT, '> smiles_2.cs1' ) or die "Cannot write to smiles_2.cs1: $!";
+binmode( IN );
+binmode( OUT );
+$cs->fh_crypt( \*IN, \*OUT, 1 );
+close IN;
+close OUT;
+
+open( IN, 'smiles_2.cs1' ) or die "Cannot read smiles_2.cs1: $!";
+open( OUT, '> smiles_2.png' ) or die "Cannot write to smiles_2.png $!";
+binmode( IN );
+binmode( OUT );
+$cs->fh_crypt( \*IN, \*OUT );
+close IN;
+close OUT;
+
+open( SOURCE, 'smiles.png' ) or die "Cannot read smiles.png: $!";
+open( DEST, 'smiles_2.png' ) or die "Cannot read smiles_2.png: $!";
+
+binmode SOURCE;
+binmode DEST;
+
+$status = 0;
+while (<SOURCE>)
+{
+ unless ($_ eq <DEST>)
+ {
+ $status = 1;
+ last;
+ }
+}
+
+ok( ! $status, 'autogenerating and autoreading IV should also round-trip' );
+
+END
+{
+ 1 while unlink qw( smiles_2.cs1 smiles_2.png outsmiles.cs1 outsmiles.png );
+}
diff --git a/t/smiles.cs1 b/t/smiles.cs1
new file mode 100644
index 0000000..135aa76
--- /dev/null
+++ b/t/smiles.cs1
Binary files differ
diff --git a/t/smiles.png b/t/smiles.png
new file mode 100644
index 0000000..3962d32
--- /dev/null
+++ b/t/smiles.png
Binary files differ