Absolute hinky bare-bones implementation of multiformats in Perl
at main 185 lines 5.7 kB view raw
1package 2 Multiformats::Multihash { 3 use strict; 4 use warnings; 5 use feature 'signatures'; 6 7 use Exporter 'import'; 8 our @EXPORT_OK = qw/multihash_encode multihash_decode multihash_wrap multihash_unwrap multihash_unwrap_stream/; 9 10 use Digest::SHA qw/sha1 sha256 sha384 sha512/; # SHA2 11 use Digest::SHA3 qw/sha3_224 sha3_384 sha3_256/; 12 use Multiformats::Varint qw/varint_decode_raw varint_encode varint_decode_stream/; 13 14 sub decode($self, $value) { 15 return multihash_decode($value); 16 } 17 18 sub encode($self, $as, $value) { 19 return multihash_encode($as, $value); 20 } 21 22 sub wrap($self, $as, $value) { 23 return multihash_wrap($as, $value); 24 } 25 26 sub unwrap($self, $value) { 27 return multihash_unwrap($value); 28 } 29 30 sub new($pkg) { 31 return bless({}, $pkg); 32 } 33 34 # this map holds the various encodings and decodings 35 use constant MULTIFORMAT_MAP => [ 36 [ 'identity', 0x00, undef, sub { return shift } ], 37 [ 'sha1', 0x11, undef, sub { return sha1(shift) } ], 38 [ 'sha2-256', 0x12, undef, sub { return sha256(shift) } ], 39 [ 'sha2-512', 0x13, undef, sub { return sha512(shift) } ], 40 [ 'sha3-384', 0x15, undef, sub { return sha3_384(shift) } ], 41 [ 'sha3-256', 0x16, undef, sub { return sha3_256(shift) } ], 42 [ 'sha3-224', 0x17, undef, sub { return sha3_224(shift) } ], 43 [ 'sha2-384', 0x20, undef, sub { return sha_384(shift) } ], 44 ]; 45 46 sub _map_by_tag($tag) { 47 foreach my $entry (@{__PACKAGE__->MULTIFORMAT_MAP}) { 48 return $entry if($entry->[1] == $tag); 49 } 50 return undef; 51 } 52 53 sub _map_by_name($name) { 54 foreach my $entry (@{__PACKAGE__->MULTIFORMAT_MAP}) { 55 return $entry if($entry->[0] eq $name); 56 } 57 return _map_by_tag($name); 58 } 59 60 sub multihash_decode($bytes) { 61 # make sure it's actual bytes 62 utf8::downgrade($bytes, 1); 63 64 my ($t, $bread_type) = varint_decode_raw($bytes); 65 if(my $e = _map_by_tag($t)) { 66 my ($l, $bread_len) = varint_decode_raw(substr($bytes, $bread_type)); 67 return substr($bytes, $bread_type + $bread_len); # there isn't any decoding since hashes are a one-way street so we just return the actual value 68 } else { 69 die 'unknown format ' . $t . ', '; 70 } 71 } 72 73 sub multihash_unwrap_stream($stream) { 74 my ($t, $bread_type) = varint_decode_stream($stream); 75 if(my $e = _map_by_tag($t)) { 76 my ($l, $bread_len) = varint_decode_stream($stream); 77 my $buf; 78 $stream->read($buf, $l); # the raw digest 79 return wantarray 80 ? ([$e->[0], $e->[1] ], $buf) # allows us to get the whole kit and kaboodle in one sitting 81 : $buf 82 } else { 83 die 'unknown format ' . $t . ', '; 84 } 85 } 86 87 sub multihash_unwrap($bytes) { 88 utf8::downgrade($bytes, 1); 89 90 my ($t, $bread_type) = varint_decode_raw($bytes); 91 if(my $e = _map_by_tag($t)) { 92 my ($l, $bread_len) = varint_decode_raw(substr($bytes, $bread_type)); 93 return wantarray 94 ? ([$e->[0], $e->[1] ], substr($bytes, $bread_type + $bread_len)) # allows us to get the whole kit and kaboodle in one sitting 95 : substr($bytes, $bread_type + $bread_len) 96 } else { 97 die 'unknown format ' . $t . ', '; 98 } 99 } 100 101 sub multihash_wrap($as, $bytes) { 102 utf8::downgrade($bytes, 1); 103 if(my $e = _map_by_name($as)) { 104 return varint_encode($e->[1]) . varint_encode(length($bytes)) . $bytes; 105 } else { 106 die 'unknown format ' . $as . ', '; 107 } 108 } 109 110 sub multihash_encode($as, $bytes) { 111 utf8::downgrade($bytes, 1); 112 if(my $e = _map_by_name($as)) { 113 my $hash = $e->[3]->($bytes); 114 return varint_encode($e->[1]) . varint_encode(length($hash)) . $hash; 115 } else { 116 die 'unknown format ' . $as . ', '; 117 } 118 } 119} 120 121=pod 122 123=head1 NAME 124 125Multiformats::Multihash - Multihash encoding/decoding/wrapping 126 127=head1 SYNOPSIS 128 129 use Multiformats::Multihash qw/multihash_encode multihash_decode multihash_unwrap multihash_wrap/; 130 my $data = '...'; 131 132 my $encoded = multihash_encode('sha2-256', $data); 133 134 my $hash = Digest::SHA::sha256($data); 135 136 my $encoded_also = multihash_wrap('sha2-256', $hash); 137 138=head1 FUNCTIONS 139 140=head2 multihash_encode($hash_function, $data) 141 142Hashes the data with the given hash function, and encodes the result into a Multihash 143 144=head2 multihash_decode($data) 145 146Parses the used hash function and hash length for validity, and returns the original raw hash 147 148=head2 multihash_unwrap($data) 149 150Acts similar to C<multihash_decode> when called in scalar context, but when called in list context returns a list containing the encoding/decoding array and the raw hash. The decoding arrayref has the hash function name as first element, and the hash function tag value as the second value. 151 152 my ($encoding, $raw_hash) = multihash_unwrap($data) 153 154 $encoding->[0]; # e.g. 'sha2-256' 155 $encoding->[1]; # e.g. 0x12 156 157=head2 multihash_wrap($hash_function, $data) 158 159Acts similar to C<multihash_encode> but assumes the data passed is already a raw hash so does not digest it before encoding to a Multihash 160 161=head1 SUPPORTED HASHES 162 163=over 164 165=item * identity 166 167=item * sha1 168 169=item * sha2-256 170 171=item * sha2-512 172 173=item * sha3-384 174 175=item * sha3-256 176 177=item * sha3-224 178 179=item * sha2-384 180 181=back 182 183=cut 184 1851;