Code:
#!/usr/bin/env perl
#
# Author: philsmd
# Date: August 2017
# License: public domain
#
use strict;
use warnings;
use MIME::Base64;
#
# Constants:
#
my $ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
#
# Helper functions:
#
sub convert_line
{
my $line = shift;
my $format = shift;
my $out = "";
if ($format == 1) # convert from Hashcat to jtr
{
my (undef, $N, $r, $p, $salt_base64, $digest_base64) = split (":", $line);
return "" if (! defined ($salt_base64));
return "" if (length ($salt_base64) < 1);
return "" if (! defined ($digest_base64));
return "" if (length ($digest_base64) < 1);
my $salt = decode_base64 ($salt_base64);
my $hash = decode_base64 ($digest_base64);
$hash = unpack ("H*", $hash);
$hash = hex_to_jtr ($hash);
$hash = substr ($hash, 0, 43);
# N
my $N_conv = hex_to_jtr (decimal_to_hex (log ($N) / log (2)));
$N_conv = substr ($N_conv, 0, 1);
# r
my $r_hex = decimal_to_hex ($r);
my $r_conv = uint32_to_jtr_conv ($r_hex); # pad to at least "30 bit"
$r_conv = hex_to_jtr ($r_conv);
$r_conv = substr ($r_conv, 0, 5); # truncate to 30 bit
# p
my $p_hex = decimal_to_hex ($p);
my $p_conv = uint32_to_jtr_conv ($p_hex);
$p_conv = hex_to_jtr ($p_conv);
$p_conv = substr ($p_conv, 0, 5);
$out = sprintf ("\$7\$%s%s%s%s\$%s", $N_conv, $r_conv, $p_conv, $salt, $hash);
}
elsif ($format == 2) # convert from Jtr to Hashcat
{
my $index_last = rindex ($line, "\$");
my $hash = substr ($line, $index_last + 1);
my $settings = substr ($line, 3, 11);
my $index = index ($line, "\$", 3);
my $salt = substr ($line, 14, $index_last - 14);
my $N_conv = substr ($settings, 0, 1);
my $r_conv = substr ($settings, 1, 5);
my $p_conv = substr ($settings, 6, 5);
# N
my $N = jtr_to_hex ($N_conv);
$N = 1 << uint32_from_jtr_conv ($N);
# r
my $r = jtr_to_hex ($r_conv);
$r = uint32_from_jtr_conv ($r);
# p
my $p = jtr_to_hex ($p_conv);
$p = uint32_from_jtr_conv ($p);
# convert hash
my $hash_conv = jtr_to_hex ($hash);
$hash_conv = substr ($hash_conv, 0, 64);
my $hash_base64 = encode_base64 (pack ("H*", $hash_conv));
$hash_base64 =~ s/[\r\n]//g;
#convert salt
my $salt_base64 = encode_base64 ($salt);
$salt_base64 =~ s/[\r\n]//g;
$out = sprintf ("SCRYPT:%i:%i:%i:%s:%s", $N, $r, $p, $salt_base64, $hash_base64);
}
return $out . "\n";
}
# Return codes - Scrypt formats
# 1 = Hashcat - Example: SCRYPT:16384:8:1:U29kaXVtQ2hsb3JpZGU=:cCO9yzr9c0hGHAbNgf046/2o+7qQT44+qbVD9lRdofI=
# 2 = Jtr - Example: $7$C6..../....SodiumChloride$kBGj9fHznVYFQMEn/qDCfrDevf9YDtcDdKvEqHJLV8D
sub detect_format
{
my $line = shift ;
my $ret = 0;
if (substr ($line, 0, 3) eq "\$7\$")
{
$ret = 2;
}
elsif (substr ($line, 0, 7) eq "SCRYPT:")
{
$ret = 1
}
return $ret;
}
sub jtr_to_hex
{
my $str = shift;
my $out = "";
my $len = length ($str);
for (my $i = 0; $i < $len; $i += 4)
{
my @nums = ();
for (my $j = $i, my $k = 0; $k < 4; $j++, $k++)
{
if ($j > $len) # exception
{
$nums[$k] = 0;
}
else
{
$nums[$k] = index ($ITOA64, substr ($str, $j, 1));
}
}
my $y = $nums[0] | ($nums[1] << 6) | ($nums[2] << 12) | ($nums[3] << 18);
my $y1 = $y & 0xff;
my $y2 = ($y >> 8) & 0xff;
my $y3 = ($y >> 16) & 0xff;
$out .= sprintf ("%02x%02x%02x", $y1, $y2, $y3)
}
return $out;
}
sub hex_to_jtr
{
my $hex = shift;
my $out = "";
my $len = length ($hex);
for (my $i = 0; $i < $len; $i += 3 * 2)
{
my @nums = ();
for (my $j = $i, my $k = 0; $k < 3; $j += 2, $k++)
{
if ($j > $len) # exception
{
$nums[$k] = 0;
}
else
{
$nums[$k] = hex (substr ($hex, $j, 2));
}
}
my $y = $nums[0] | ($nums[1] << 8) | ($nums[2] << 16);
my $x1 = $y & 0x3f;
my $x2 = ($y >> 6) & 0x3f;
my $x3 = ($y >> 12) & 0x3f;
my $x4 = ($y >> 18) & 0x3f;
$out .= substr ($ITOA64, $x1, 1);
$out .= substr ($ITOA64, $x2, 1);
$out .= substr ($ITOA64, $x3, 1);
$out .= substr ($ITOA64, $x4, 1);
}
return $out;
}
sub decimal_to_hex
{
my $num = shift;
$num = sprintf ("%x", $num);
$num = "0" . $num unless ((length ($num) % 2) == 0);
return $num;
}
# 1. left-to-right to right-to-left
# 2. pad to at least 30 bit (~ 4 x 8 bit blocks)
sub uint32_to_jtr_conv
{
my $hex = shift;
my $out = "";
my $len = length ($hex);
for (my $i = 0; $i < $len; $i += 2)
{
$out .= substr ($hex, $len - $i - 2, 2);
}
for (my $i = $len / 2; $i < 4; $i++)
{
$out .= "00";
}
return $out;
}
# 1. revert: right-to-left -> left-to-right
# (while skipping 00 at beginning (== end))
sub uint32_from_jtr_conv
{
my $hex = shift;
my $out = "";
my $len = length ($hex);
my $is_beginning = 1;
for (my $i = 0; $i < $len; $i += 2)
{
my $num = substr ($hex, $len - $i - 2, 2);
next if ($is_beginning && ($num eq "00"));
$is_beginning = 0;
$out .= $num;
}
return hex ($out);
}
#
# Start
#
my $INPUT = "-";
if ($ARGV[0])
{
$INPUT = $ARGV[0];
}
my $detected_format = 0;
open (INFILE, "<" . $INPUT) or die ("ERROR: could not open input file");
while (my $line = <INFILE>)
{
chomp $line;
if ($detected_format == 0)
{
$detected_format = detect_format ($line);
if ($detected_format == 0)
{
print STDERR "ERROR: could not detect hash format. EXIT\n";
last;
}
}
print STDOUT convert_line ($line, $detected_format);
}
close (INFILE);