ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-FCP/FCP/Key/CHK.pm
Revision: 1.7
Committed: Wed Jan 12 20:37:33 2005 UTC (21 years, 8 months ago) by root
Branch: MAIN
Changes since 1.6: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3     Net::FCP::Key::CHK - manage CHK keys.
4    
5     =head1 SYNOPSIS
6    
7     use Net::FCP::Key::CHK;
8    
9     my $key = new Net::FCP::Key::CHK;
10     my $key = new_from_uri Net::FCP::Key::CHK $uri;
11     my $key = new_from_data Net::FCP::Key::CHK $data, $metadata;
12     ... more to come
13    
14    
15     =head1 DESCRIPTION
16    
17     =head2 THE Net::FCP::Key::CHK CLASS
18    
19     =over 4
20    
21     =cut
22    
23     package Net::FCP::Key::CHK;
24    
25     use Carp;
26     use Digest::SHA1;
27 root 1.3 use MIME::Base64;
28    
29     use Crypt::Rijndael;
30 root 1.2 use Crypt::Twofish;
31 root 1.1
32 root 1.2 use Net::FCP::Util;
33    
34 root 1.1 no warnings;
35    
36     =item my $key = new Net::FCP::Key::CHK;
37    
38 root 1.5 Heavily under development, don't use :)
39 root 1.1
40     =cut
41    
42     sub new {
43     my $class = shift;
44    
45     bless { }, $class;
46     }
47    
48 root 1.6 =item my $key = new_from_data Net::FCP::Key::CHK $metadata, $data[, $cipher];
49 root 1.1
50     Generate a CHK from the given data and metadata strings.
51    
52     =cut
53    
54     sub new_from_data {
55 root 1.3 my ($class, $metadata, $data, $cipher) = @_;
56 root 1.1
57 root 1.3 $class->new->set_data ($metadata, $data, $cipher);
58 root 1.2 }
59    
60 root 1.3 sub rolling_hashpad($$$) {
61     my $sha1 = $_[1];
62 root 1.2 my $pad = "";
63 root 1.3 my $dig;
64 root 1.2
65 root 1.3 while ($_[2] > length $_[0]) {
66     $sha1->add ($dig = $sha1->digest_noreset);
67 root 1.2 $pad .= $dig;
68     $_[0] .= $pad;
69     }
70    
71 root 1.3 substr $_[0], $_[2], length $_[0], "";
72 root 1.2 }
73    
74     sub encode_number($) {
75     my $num = pack "N", $_[0];
76     $num =~ s/^\x00+//;
77     pack "n a*", length $num, $num;
78 root 1.1 }
79    
80     sub set_data {
81 root 1.3 my ($self, $metadata, $data, $cipher) = @_;
82    
83     $cipher ||= "Twofish";
84    
85     my $cipher_class = "Crypt::$cipher";
86    
87     $cipher_class->blocksize == 16 or die "only ciphers with a blocksize of 128 bits are supported";
88 root 1.2
89     my $total_len = (length $metadata) + (length $data);
90 root 1.3 my $padded_log = Net::FCP::Util::log2 $total_len, 10;
91     my $padded_len = 1 << $padded_log;
92 root 1.2
93     my $plaintext = "$metadata$data";
94 root 1.1
95 root 1.3 # crypto key (hash) generation. this is an iterative
96     # algorithm, but it is "unrolled" here for the
97     # common keysize of 16 bytes.
98     my $data_sha1 = Digest::SHA1->new->add ($plaintext);
99 root 1.1
100     # only works for 128 bit keys
101     my $k = new Digest::SHA1;
102     $k->add ("\x00" x 1);
103 root 1.3 $k->add ($data_sha1->clone->digest);
104 root 1.2
105 root 1.3 my $hash = substr $k->digest, 0, 16; # extract leading 128 bit
106 root 1.2
107     my $buf = "";
108    
109     $buf .= pack "n a20", 20, Digest::SHA1::sha1 $hash;
110    
111     $buf .= encode_number $total_len;
112     $buf .= encode_number length $metadata;
113    
114     $buf .= "\x00\x00";
115    
116 root 1.3 rolling_hashpad $buf, Digest::SHA1->new->add ($buf), 1 << Net::FCP::Util::log2 length $buf;
117 root 1.2
118 root 1.4 my $pcfb_cipher = $cipher_class->new ($hash);
119     my $pcfb_reg = "\x00" x 16;
120 root 1.2
121     my $pcfb_enc = sub {
122 root 1.3 my $length = length $_[0];
123 root 1.4 my $enc = "";
124 root 1.3 for (my $i = 0; $i < $length; $i += 16) {
125 root 1.4 $enc .= $pcfb_reg = $pcfb_cipher->encrypt ($pcfb_reg) ^ substr $_[0], $i, 16;
126 root 1.2 }
127 root 1.4 $enc;
128 root 1.2 };
129    
130 root 1.3 # buf length must be multiple of 16
131 root 1.4 $buf = $pcfb_enc->($buf);
132 root 1.2
133     my $senc = unpack "H*", $buf;
134    
135 root 1.3 rolling_hashpad $plaintext, $data_sha1, $padded_len;
136 root 1.2
137 root 1.3 # plaintext length must be a multiple of 16, too
138 root 1.4 $plaintext = $pcfb_enc->($plaintext);
139 root 1.3
140     my $partsize = $padded_len < 16384 ? $padded_len
141     : $padded_len < 16384 << 7 ? 16384
142     : $padded_len >> 7; # 2MB
143 root 1.2
144     my $dig = "";
145     for (my $ofs = ($padded_len-1) - ($padded_len-1) % $partsize; $ofs >= 0; $ofs -= $partsize) {
146     $dig = Digest::SHA1::sha1 substr ($plaintext, $ofs, $partsize) . $dig;
147     }
148    
149     my $sini = unpack "H*", $dig;
150    
151 root 1.3 my $route = sprintf
152 root 1.2 "Document-header\xfe%s\xff"
153     . "Initial-digest\xfe%s\xff"
154     . "Part-size\xfe%x\xff"
155     . "Symmetric-cipher\xfe%s\xff",
156 root 1.3 $senc, $sini, $partsize, $cipher;
157 root 1.2
158 root 1.3 $route = Net::FCP::Util::encode_base64 +(Digest::SHA1::sha1 $route) . (pack "C", $padded_log) . "\x03\x02";
159     $hash = Net::FCP::Util::encode_base64 $hash;
160 root 1.2
161 root 1.3 return "freenet:CHK\@$route,$hash";
162 root 1.2 $self;
163 root 1.1 }
164    
165     =item $size = $key->size
166    
167     Returns the size of the data (in bytes).
168    
169     =cut
170    
171     =item $digest = $key->digest
172    
173     Return the store digest/hash.
174    
175     =cut
176    
177     =item $keynum = $key->keynumber
178    
179     Returns the keynumber (version?)
180    
181     =cut
182    
183     =item $chk = $key->chk
184    
185     Return the full CHK.
186    
187     =cut
188    
189     =back
190    
191     =head1 SEE ALSO
192    
193     L<Net::FCP>.
194    
195     =head1 BUGS
196    
197     Not heavily tested.
198    
199     =head1 AUTHOR
200    
201     Marc Lehmann <pcg@goof.com>
202 root 1.7 http://home.schmorp.de/
203 root 1.1
204     =cut
205    
206     1;
207