1#!/usr/bin/env perl 2 3## 4## Author......: See docs/credits.txt 5## License.....: MIT 6## 7 8use strict; 9use warnings; 10 11use Crypt::GCrypt; 12use Crypt::PBKDF2; 13use Digest::SHA qw (sha1 sha1_hex); 14 15sub module_constraints { [[0, 51], [32, 32], [-1, -1], [-1, -1], [-1, -1]] } 16 17sub module_generate_hash 18{ 19 my $word = shift; 20 my $salt = shift; 21 my $iter = shift // 100000; 22 my $iv = shift // random_hex_string (2 * 8); 23 my $plain = shift // random_hex_string (2 * 1024); 24 25 my $b_iv = pack ('H*', $iv); 26 my $b_salt = pack ('H*', $salt); 27 my $b_plain = pack ('H*', $plain); 28 29 my $kdf = Crypt::PBKDF2->new 30 ( 31 hash_class => 'HMACSHA1', 32 iterations => $iter, 33 output_len => 16 34 ); 35 36 my $pass_hash = sha1 ($word); 37 my $key = $kdf->PBKDF2 ($b_salt, $pass_hash); 38 39 my $cfb = Crypt::GCrypt->new 40 ( 41 type => 'cipher', 42 algorithm => 'blowfish', 43 mode => 'cfb' 44 ); 45 46 $cfb->start ('encrypting'); 47 $cfb->setkey ($key); 48 $cfb->setiv ($b_iv); 49 50 my $b_cipher = $cfb->encrypt ($b_plain); 51 52 $cfb->finish (); 53 54 my $cipher = unpack ('H*', $b_cipher); 55 my $checksum = sha1_hex ($b_plain); 56 57 my $hash = '$odf$' . "*0*0*$iter*16*$checksum*8*$iv*16*$salt*0*$cipher"; 58 59 return $hash; 60} 61 62sub module_verify_hash 63{ 64 my $line = shift; 65 66 my ($hash, $word) = split (':', $line); 67 68 return unless defined $hash; 69 return unless defined $word; 70 71 my $word_packed = pack_if_HEX_notation ($word); 72 73 # tokenize 74 my @data = split ('\*', $hash); 75 76 return unless scalar @data == 12; 77 78 my $signature = shift @data; 79 my $cipher_type = shift @data; 80 my $cs_type = shift @data; 81 my $iter = shift @data; 82 my $cs_len = shift @data; 83 my $cs = shift @data; 84 my $iv_len = shift @data; 85 my $iv = shift @data; 86 my $salt_len = shift @data; 87 my $salt = shift @data; 88 my $unused = shift @data; 89 my $cipher = shift @data; 90 91 # validate 92 return unless $signature eq '$odf$'; 93 return unless $cipher_type eq '0'; 94 return unless $cs_type eq '0'; 95 return unless $cs_len eq '16'; 96 return unless $iv_len eq '8'; 97 return unless $salt_len eq '16'; 98 return unless $unused eq '0'; 99 return unless defined $cipher; 100 101 # decrypt 102 my $b_iv = pack ('H*', $iv); 103 my $b_salt = pack ('H*', $salt); 104 my $b_cipher = pack ('H*', $cipher); 105 106 my $kdf = Crypt::PBKDF2->new 107 ( 108 hash_class => 'HMACSHA1', 109 iterations => $iter, 110 output_len => 16 111 ); 112 113 my $pass_hash = sha1 ($word); 114 my $key = $kdf->PBKDF2 ($b_salt, $pass_hash); 115 116 my $cfb = Crypt::GCrypt->new ( 117 type => 'cipher', 118 algorithm => 'blowfish', 119 mode => 'cfb' 120 ); 121 122 $cfb->start ('decrypting'); 123 $cfb->setkey ($key); 124 $cfb->setiv ($b_iv); 125 126 my $b_plain = $cfb->decrypt ($b_cipher); 127 128 $cfb->finish (); 129 130 my $plain = unpack ('H*', $b_plain); 131 132 my $new_hash = module_generate_hash ($word_packed, $salt, $iter, $iv, $plain); 133 134 return ($new_hash, $word); 135} 136 1371; 138