| Net-FCP documentation | Contained in the Net-FCP distribution. |
Net::FCP::Key::CHK - manage CHK keys.
use Net::FCP::Key::CHK; my $key = new Net::FCP::Key::CHK; my $key = new_from_uri Net::FCP::Key::CHK $uri; my $key = new_from_data Net::FCP::Key::CHK $data, $metadata; ... more to come
Heavily under development, don't use :)
Generate a CHK from the given data and metadata strings.
Returns the size of the data (in bytes).
Return the store digest/hash.
Returns the keynumber (version?)
Return the full CHK.
Not heavily tested.
Marc Lehmann <schmorp@schmorp.de> http://home.schmorp.de/
| Net-FCP documentation | Contained in the Net-FCP distribution. |
package Net::FCP::Key::CHK; use Carp; use Digest::SHA1; use MIME::Base64; use Crypt::Rijndael; use Crypt::Twofish; use Net::FCP::Util; no warnings;
sub new { my $class = shift; bless { }, $class; }
sub new_from_data { my ($class, $metadata, $data, $cipher) = @_; $class->new->set_data ($metadata, $data, $cipher); } sub rolling_hashpad($$$) { my $sha1 = $_[1]; my $pad = ""; my $dig; while ($_[2] > length $_[0]) { $sha1->add ($dig = $sha1->digest_noreset); $pad .= $dig; $_[0] .= $pad; } substr $_[0], $_[2], length $_[0], ""; } sub encode_number($) { my $num = pack "N", $_[0]; $num =~ s/^\x00+//; pack "n a*", length $num, $num; } sub set_data { my ($self, $metadata, $data, $cipher) = @_; $cipher ||= "Twofish"; my $cipher_class = "Crypt::$cipher"; $cipher_class->blocksize == 16 or die "only ciphers with a blocksize of 128 bits are supported"; my $total_len = (length $metadata) + (length $data); my $padded_log = Net::FCP::Util::log2 $total_len, 10; my $padded_len = 1 << $padded_log; my $plaintext = "$metadata$data"; # crypto key (hash) generation. this is an iterative # algorithm, but it is "unrolled" here for the # common keysize of 16 bytes. my $data_sha1 = Digest::SHA1->new->add ($plaintext); # only works for 128 bit keys my $k = new Digest::SHA1; $k->add ("\x00" x 1); $k->add ($data_sha1->clone->digest); my $hash = substr $k->digest, 0, 16; # extract leading 128 bit my $buf = ""; $buf .= pack "n a20", 20, Digest::SHA1::sha1 $hash; $buf .= encode_number $total_len; $buf .= encode_number length $metadata; $buf .= "\x00\x00"; rolling_hashpad $buf, Digest::SHA1->new->add ($buf), 1 << Net::FCP::Util::log2 length $buf; my $pcfb_cipher = $cipher_class->new ($hash); my $pcfb_reg = "\x00" x 16; my $pcfb_enc = sub { my $length = length $_[0]; my $enc = ""; for (my $i = 0; $i < $length; $i += 16) { $enc .= $pcfb_reg = $pcfb_cipher->encrypt ($pcfb_reg) ^ substr $_[0], $i, 16; } $enc; }; # buf length must be multiple of 16 $buf = $pcfb_enc->($buf); my $senc = unpack "H*", $buf; rolling_hashpad $plaintext, $data_sha1, $padded_len; # plaintext length must be a multiple of 16, too $plaintext = $pcfb_enc->($plaintext); my $partsize = $padded_len < 16384 ? $padded_len : $padded_len < 16384 << 7 ? 16384 : $padded_len >> 7; # 2MB my $dig = ""; for (my $ofs = ($padded_len-1) - ($padded_len-1) % $partsize; $ofs >= 0; $ofs -= $partsize) { $dig = Digest::SHA1::sha1 substr ($plaintext, $ofs, $partsize) . $dig; } my $sini = unpack "H*", $dig; my $route = sprintf "Document-header\xfe%s\xff" . "Initial-digest\xfe%s\xff" . "Part-size\xfe%x\xff" . "Symmetric-cipher\xfe%s\xff", $senc, $sini, $partsize, $cipher; $route = Net::FCP::Util::encode_base64 +(Digest::SHA1::sha1 $route) . (pack "C", $padded_log) . "\x03\x02"; $hash = Net::FCP::Util::encode_base64 $hash; return "freenet:CHK\@$route,$hash"; $self; }
1;