67 lines
2.0 KiB
Raku
67 lines
2.0 KiB
Raku
sub md4(@) {
|
|
my @input = grep { defined && length > 0 } split /(.{64})/s, join '', @_;
|
|
push @input, '' if !@input || length($input[$#input]) >= 56;
|
|
|
|
my @A = (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476); # initial regs
|
|
my @T = (0, 0x5A827999, 0x6ED9EBA1);
|
|
my @L = qw(3 7 11 19 3 5 9 13 3 9 11 15); # left rotate counts
|
|
my @O = (1, 4, 4, # x stride for input index
|
|
4, 1, 1, # y stride for input index
|
|
0, 0, 1); # bitwise reverse both indexes
|
|
my @I = map {
|
|
my $z = int $_/16;
|
|
my $x = $_%4;
|
|
my $y = int $_%16/4;
|
|
($x,$y) = (R($x),R($y)) if $O[6+$z];
|
|
$O[$z] * $x + $O[3+$z] * $y
|
|
} 0..47;
|
|
|
|
my ($a,$b,$c,$d);
|
|
my($l,$p) = (0,0);
|
|
foreach (@input) {
|
|
my $r = length($_);
|
|
$l += $r;
|
|
$r++, $_.="\x80" if $r<64 && !$p++;
|
|
my @W = unpack 'V16', $_ . "\0"x7;
|
|
push @W, (0)x16 if @W < 16;
|
|
$W[14] = $l*8 if $r < 57; # add bit-length in low 32-bits
|
|
($a,$b,$c,$d) = @A;
|
|
for (0..47) {
|
|
my $z = int $_/16;
|
|
$a = L($L[4*($_>>4) + $_%4],
|
|
M(&{(sub{$b&$c|~$b&$d}, # F
|
|
sub{$b&$c|$b&$d|$c&$d}, # G
|
|
sub{$b^$c^$d} # H
|
|
)[$z]}
|
|
+ $a + $W[$I[$_]] + $T[$z]));
|
|
($a,$b,$c,$d) = ($d,$a,$b,$c);
|
|
}
|
|
my @v = ($a, $b, $c, $d);
|
|
$A[$_] = M($A[$_] + $v[$_]) for 0..3;
|
|
}
|
|
pack 'V4', @A;
|
|
}
|
|
|
|
sub L { # left-rotate
|
|
my ($n, $x) = @_;
|
|
$x<<$n | 2**$n - 1 & $x>>(32-$n);
|
|
}
|
|
|
|
sub M { # mod 2**32
|
|
no integer;
|
|
my ($x) = @_;
|
|
my $m = 1+0xffffffff;
|
|
$x - $m * int $x/$m;
|
|
}
|
|
|
|
sub R { # reverse two bit number
|
|
my $n = pop;
|
|
($n&1)*2 + ($n&2)/2;
|
|
}
|
|
|
|
sub md4_hex(@) { # convert to hexadecimal
|
|
unpack 'H*', &md4;
|
|
}
|
|
|
|
print "Rosetta Code => " . md4_hex( "Rosetta Code" ) . "\n";
|