ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-FCP/FCP/Key/CHK.pm
Revision: 1.3
Committed: Sun Oct 12 18:09:28 2003 UTC (22 years, 11 months ago) by root
Branch: MAIN
Changes since 1.2: +75 -39 lines
Log Message:
*** empty log message ***

File Contents

# Content
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 =back
18
19 =head2 THE Net::FCP::Key::CHK CLASS
20
21 =over 4
22
23 =cut
24
25 package Net::FCP::Key::CHK;
26
27 use Carp;
28 use Digest::SHA1;
29 use MIME::Base64;
30
31 use Crypt::Rijndael;
32 use Crypt::Twofish;
33
34 use Net::FCP::Util;
35
36 no warnings;
37
38 =item my $key = new Net::FCP::Key::CHK;
39
40 Don't use :)
41
42 =cut
43
44 sub new {
45 my $class = shift;
46
47 bless { }, $class;
48 }
49
50 =item my $key = new_from_data Net::FCP::Key::CHK $metadata, $data, $cipher;
51
52 Generate a CHK from the given data and metadata strings.
53
54 =cut
55
56 sub new_from_data {
57 my ($class, $metadata, $data, $cipher) = @_;
58
59 $class->new->set_data ($metadata, $data, $cipher);
60 }
61
62 sub rolling_hashpad($$$) {
63 my $sha1 = $_[1];
64 my $pad = "";
65 my $dig;
66
67 while ($_[2] > length $_[0]) {
68 $sha1->add ($dig = $sha1->digest_noreset);
69 $pad .= $dig;
70 $_[0] .= $pad;
71 }
72
73 substr $_[0], $_[2], length $_[0], "";
74 }
75
76 sub encode_number($) {
77 my $num = pack "N", $_[0];
78 $num =~ s/^\x00+//;
79 pack "n a*", length $num, $num;
80 }
81
82 sub set_data {
83 my ($self, $metadata, $data, $cipher) = @_;
84
85 $cipher ||= "Twofish";
86
87 my $cipher_class = "Crypt::$cipher";
88
89 $cipher_class->blocksize == 16 or die "only ciphers with a blocksize of 128 bits are supported";
90
91 my $total_len = (length $metadata) + (length $data);
92 my $padded_log = Net::FCP::Util::log2 $total_len, 10;
93 my $padded_len = 1 << $padded_log;
94
95 my $plaintext = "$metadata$data";
96
97 # crypto key (hash) generation. this is an iterative
98 # algorithm, but it is "unrolled" here for the
99 # common keysize of 16 bytes.
100 my $data_sha1 = Digest::SHA1->new->add ($plaintext);
101
102 # only works for 128 bit keys
103 my $k = new Digest::SHA1;
104 $k->add ("\x00" x 1);
105 $k->add ($data_sha1->clone->digest);
106
107 my $hash = substr $k->digest, 0, 16; # extract leading 128 bit
108
109 my $buf = "";
110
111 $buf .= pack "n a20", 20, Digest::SHA1::sha1 $hash;
112
113 $buf .= encode_number $total_len;
114 $buf .= encode_number length $metadata;
115
116 $buf .= "\x00\x00";
117
118 rolling_hashpad $buf, Digest::SHA1->new->add ($buf), 1 << Net::FCP::Util::log2 length $buf;
119
120 my $pcfbc = $cipher_class->new ($hash);
121 my $pcfbr = "\x00" x 16;
122 my $pcfb = 16;
123
124 my $pcfb_enc = sub {
125 my $length = length $_[0];
126 for (my $i = 0; $i < $length; $i += 16) {
127 substr $_[0], $i, 16, $pcfbr = $pcfbc->encrypt ($pcfbr) ^ substr $_[0], $i, 16;
128 }
129 };
130
131 # buf length must be multiple of 16
132 $pcfb_enc->($buf);
133
134 my $senc = unpack "H*", $buf;
135
136 rolling_hashpad $plaintext, $data_sha1, $padded_len;
137
138 # plaintext length must be a multiple of 16, too
139 print "<";
140 $pcfb_enc->($plaintext);
141 print ">";
142
143 my $partsize = $padded_len < 16384 ? $padded_len
144 : $padded_len < 16384 << 7 ? 16384
145 : $padded_len >> 7; # 2MB
146
147 my $dig = "";
148 for (my $ofs = ($padded_len-1) - ($padded_len-1) % $partsize; $ofs >= 0; $ofs -= $partsize) {
149 $dig = Digest::SHA1::sha1 substr ($plaintext, $ofs, $partsize) . $dig;
150 }
151
152 my $sini = unpack "H*", $dig;
153
154 my $route = sprintf
155 "Document-header\xfe%s\xff"
156 . "Initial-digest\xfe%s\xff"
157 . "Part-size\xfe%x\xff"
158 . "Symmetric-cipher\xfe%s\xff",
159 $senc, $sini, $partsize, $cipher;
160
161 $route = Net::FCP::Util::encode_base64 +(Digest::SHA1::sha1 $route) . (pack "C", $padded_log) . "\x03\x02";
162 $hash = Net::FCP::Util::encode_base64 $hash;
163
164 return "freenet:CHK\@$route,$hash";
165 $self;
166 }
167
168 =item $size = $key->size
169
170 Returns the size of the data (in bytes).
171
172 =cut
173
174 =item $digest = $key->digest
175
176 Return the store digest/hash.
177
178 =cut
179
180 =item $keynum = $key->keynumber
181
182 Returns the keynumber (version?)
183
184 =cut
185
186 =item $chk = $key->chk
187
188 Return the full CHK.
189
190 =cut
191
192 =back
193
194 =head1 SEE ALSO
195
196 L<Net::FCP>.
197
198 =head1 BUGS
199
200 Not heavily tested.
201
202 =head1 AUTHOR
203
204 Marc Lehmann <pcg@goof.com>
205 http://www.goof.com/pcg/marc/
206
207 =cut
208
209 if (0) {
210 my $data = do { local $/; <> };
211 my $metadata = substr $data, 0, 0x3f, "";
212 $metadata = "x" x 3000;
213 $data = "a" x 4098;
214
215 warn Net::FCP::Key::CHK->new_from_data ($metadata, $data);
216 use Net::FCP; my $fcp = new Net::FCP; warn $fcp->generate_chk ($metadata, $data);
217 } else {
218 use Net::FCP;
219 my $fcp = new Net::FCP;
220
221 $|=1;
222 use Time::HiRes 'time';
223 while () {
224 my $meta = join "", map chr(rand(256)), 1..(30+rand(20));
225 my $data = join "", map chr(rand(256)), 1..(20+rand(200000));
226 my $meta = join "", map chr(rand(256)), 1..1024;
227 my $data = join "", map chr(rand(256)), 1..1024000;
228 printf "%d - %d ", length $meta, length $data;
229 my $t1 = time;
230 my $b = $fcp->generate_chk ($meta, $data, "Rijndael");
231 my $t2 = time;
232 my $a = Net::FCP::Key::CHK->new_from_data ($meta, $data, "Rijndael");
233 my $t3 = time;
234 printf "%.2f %.2f ", $t2-$t1, $t3-$t2;
235 die "$a ne $b" unless "$a" eq $b;
236 print "ok\n";
237 }
238 }
239
240 1;
241