[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
PGP padding scripts (again)
Sorry for the bandwidth waste, but my anonymous correspondent was not
able to ftp these scripts. I also got another request for the scripts.
So here again is my post with a pair of perl scripts which will insert
padding pretty much undetectably into a .pgp file. The one limitation is
the quality of Perl's random number generator.
Here are a couple of perl scripts I wrote last year to add padding to PGP
encrypted files. The usage would be:
perl pgppadt.pl filename bytestoadd
The output file is filename.pad.
It only works on binary ".pgp" public-key encrypted files (not ascii armored
files). So there would be some work needed to make it a really useful tool.
It would also be better to use a strong source of random numbers. I
think Carl Ellison recently posted some tools that could help with this.
The two files are pgppad.pl, which does the work, and pgppadt.pl, a very
simple test driver to show how to use it. They are in a shar archive.
Hal
---------------cut here----------------
#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./pgppad.pl`
then
echo "writing ./pgppad.pl"
cat > ./pgppad.pl << '\End\Of\Shar\'
# Perl module to allow padding and some other manipulation of PGP
# files.
#
# Include this with the statement:
# require 'pgppad.pl'
#
# 10/16/93
# Hal Finney
# Read a PGP Cipher Type Byte and the following length.
# One argument: file to read from
# Returns several things, in this order:
# CTB, with the length information removed, as a number.
# Length of following packet.
# Name of this kind of packet, made up, see list below.
# Packed CTB/length packet, suitable for writing out.
# Returns an empty string on error.
sub read_ctb {
local($file) = @_;
local($ctb, $length, $name, $rctb, $rlength, $lengthlength);
if (read ($file, $rctb, 1) != 1) { # Raw ctb
return "";
}
$ctb = unpack ("C", $rctb);
if ($ctb < 128) {
return ""; # Must have high bit set
}
$lengthlength = $ctb % 4;
$ctb -= $lengthlength;
if ($lengthlength == 0) {
$lengthlength = 1;
} elsif ($lengthlength == 1) {
$lengthlength = 2;
} elsif ($lengthlength == 2) {
$lengthlength = 4;
} else {
$lengthlength = 0;
$length = -1; # Unknown length
}
if (read ($file, $rlength, $lengthlength) != $lengthlength) {
return "";
}
if ($lengthlength==1) {
$length = unpack("C", $rlength);
} elsif ($lengthlength==2) {
$length = unpack("n", $rlength);
} elsif ($lengthlength==4) {
$length = unpack("N", $rlength);
}
$rctb = pack ("C a".$lengthlength, $rctb, $rlength); # Packed data
if ($ctb==0x84) {
$name = "pubkey header";
} elsif ($ctb==0x88) {
$name = "signature";
} elsif ($ctb==0x8c) {
$name = "message digest";
} elsif ($ctb==0x94) {
$name = "secret key";
} elsif ($ctb==0x98) {
$name = "public key";
} elsif ($ctb==0xa0) {
$name = "compressed";
} elsif ($ctb==0xa4) {
$name = "conventional encrypted";
} elsif ($ctb==0xa8) {
$name = "plaintext";
} elsif ($ctb==0xb0) {
$name = "trust";
} elsif ($ctb==0xb4) {
$name = "user id";
} elsif ($ctb==0xb8) {
$name = "comment";
} else {
return "";
}
return ($ctb, $length, $name, $rctb);
}
# Write a CTB and length field out.
# 3 arguments: file handle, ctb value, and length in bytes.
# No return value.
# Length gets output as 1, 2, or 4 bytes, the smallest in which it
# will fit.
# If length is negative we output no length field, but an "indefinite
# length" code is added to ctb.
sub write_ctb {
local($file, $ctb, $length) = @_;
local($rctb);
$ctb = $ctb - ($ctb % 4); # Be sure 2 low bits are clear
if ($length < 0) {
$rctb = pack ("C", $ctb+3); # Packed data
} elsif ($length > 65535) {
$rctb = pack ("C N", $ctb+2, $length); # Packed data
} elsif ($length > 255) {
$rctb = pack ("C n", $ctb+1, $length); # Packed data
} else {
$rctb = pack ("C C", $ctb+0, $length); # Packed data
}
print $file $rctb;
}
# This entry point always outputs a 4-byte count. Length must be > 0.
# Otherwise like write_ctb.
sub write_ctb_4 {
local($file, $ctb, $length) = @_;
local($rctb);
$ctb = $ctb - ($ctb % 4); # Be sure 2 low bits are clear
if ($length < 0) {
die ("write_ctb_4 called with negative length\n");
}
$rctb = pack ("C N", $ctb+2, $length); # Packed data
print $file $rctb;
}
# Pad a PGP public-key-encrypted file to the specified length.
# Arguments: input file handle; output file handle; new size.
# Returns negative value on error. See the code for what the
# different values mean.
# Returns 0 on success.
sub pgppad {
local($infile, $outfile, $size) = @_;
local($ctb, $length, $name, $rctb, $insize, $buf);
# Read ctb & length of pubkey header
($ctb, $len, $name, $rctb) = &read_ctb($infile);
if ($ctb == 0) {
return -1; # Error
}
if ($name ne "pubkey header") {
return -2; # Error
}
if ($len < 0) {
return -3; # Error
}
$insize = length($rctb) + $len;
# Read packet of pubkey header
if (read ($infile, $data, $len) != $len) {
return -3;
}
# Write out pubkey header, unchanged
&write_ctb($outfile, $ctb, $len);
print $outfile $data;
# Read ctb and length of conventional packet
($ctb, $len, $name, $rctb) = &read_ctb($infile);
if ($ctb == 0) {
return -4; # Error
}
if ($name ne "conventional encrypted") {
return -5; # Error
}
# Calculate size of outgoing conventional packet.
# Assume rctb won't change size; it may grow by 1 or 2 in some
# rather rare cases, in which case we'll be a byte or two too big.
$size -= $insize + length($rctb);
if ($size < $len) {
return -6; # Error
}
# Output CTB with new length
&write_ctb_4($outfile, $ctb, $size);
# Copy remainder of input file
while (read ($infile, $buf, 32768)) {
print $outfile $buf;
}
# Note that this random number generator is probably not
# cryptographically strong.
srand (time|$$);
while ($len < $size) {
print $outfile pack ("C", int(rand(256)));
++$len;
}
return 0; # Success
}
1; # Non-zero return for 'require'
\End\Of\Shar\
else
echo "will not over write ./pgppad.pl"
fi
if `test ! -s ./pgppadt.pl`
then
echo "writing ./pgppadt.pl"
cat > ./pgppadt.pl << '\End\Of\Shar\'
# Test program for pgppad.pl, showing how to use it.
require 'pgppad.pl';
open (IN, $ARGV[0]) || die ("Couldn't open $ARGV[0]\n");
open (OUT, ">$ARGV[0].pad") || die ("Couldn't create $ARGV[0].pad\n");
$padding = $ARGV[1];
@stat = stat(IN);
$size = $stat[7];
print "Input file $ARGV[0] has size $size bytes\n";
print "Output file $ARGV[0].pad will have size ".$size+$padding." bytes\n";
if (($code = &pgppad (IN, OUT, $size+$padding)) < 0) {
die ("pgppad returns code $code\n");
}
close (IN);
close (OUT);
print ("Done\n");
\End\Of\Shar\
else
echo "will not over write ./pgppadt.pl"
fi
echo "Finished archive 1 of 1"
exit