TL;DR

Sometimes I wish these challenges elaborated a bit more on the goals and the morale of whatâ€™s requested. You know, just to make sure that Iâ€™m not missing the full range of lessons.

In this case, the title says it pretty straight: we have to implement a dictionary attack. Then the text asks us to crack the password. So the immediate lessons I get even before starting are:

• SRP is prone to be attacked with a dictionary attack
• time and again, weâ€™re proven that a weak password defies any effort.

The first deduction comes from the fact that weâ€™re asked to implement the dictionary attack, so it should be feasible right?

In this case, it relies on the fact that all involved operations â€“which eventually boil down to exponentiations modulo a prime and SHA-256 calculationsâ€“ can be easily implemented in a very fast way. Maybe this is nudging us towards something which requires much more to calculated, e.g. ARGON2.

Anyway.

I took it literally and maybe cheated a bit, but for a good cause. To make a dictionary attack successful, we need to have a dictionary. So I took the first one I found around, i.e. /usr/share/dict/words to both choose a single word as a password and to try them all for cracking.

#!/usr/bin/env perl
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';

use File::Basename 'dirname';
use lib dirname(__FILE__);
use CryptoPals qw< slurp sha256 hmac_sha256 >;
use DiffieHellman;
use Math::BigInt;
use Math::Prime::Util qw< powmod mulmod >;

use Digest;

$|++; my$g = DiffieHellman::NIST_g;
my $n = DiffieHellman::NIST_p; my$words_file = shift // '/usr/share/dict/words';
my @words = split m{\n+}mxs, slurp($words_file); # MITM server... Settle for simple params my$srv_b = 1;
my $srv_B = Math::BigInt->new($g); # $g **$srv_b = $g my$srv_u = Math::BigInt->new(1);  # don't bother
my $srv_salt = ''; # client, let's do this "correctly". my ($clt_A, $clt_mac); my$is_password_correct;
{
my $client_password =$words[rand @words];
say "pssst! client chose password '$client_password', KEEP IT SAFE!";$is_password_correct = sub ($p) {$p eq $client_password }; my$dh = DiffieHellman->new(p => $n, g =>$g);
my ($clt_a_o,$clt_A_o) = $dh->generate_key_pair; my$clt_a = Math::BigInt->from_hex(unpack 'H*', $clt_a_o);$clt_A = Math::BigInt->from_hex(unpack 'H*', $clt_A_o); my$x_hex = unpack 'H*', sha256($srv_salt .$client_password);
my $x = Math::BigInt->from_hex($x_hex);
my $S = modexp($srv_B, $clt_a +$srv_u * $x,$n);
$clt_mac = hmac_sha256(sha256($S), $srv_salt); } # MITM side after receiving$clt_A and $clt_mac for my$word (@words) {
print {*STDOUT} "\r" . (' ' x 60) . "\rTrying '$word'"; my$x = Math::BigInt->from_hex(unpack 'H*', sha256($word)); my$S = ($clt_A * modexp($g, $x,$n)) % $n; my$crk_mac = hmac_sha256(sha256($S),$srv_salt);
if ($crk_mac eq$clt_mac) {
say "\nfound word '$word' and it is ",$is_password_correct->($word) ? 'correct' : 'WRONG!!!'; last; } } sub modexp ($b, $e,$n) { Math::BigInt->new(Math::Prime::Util::powmod($b,$e, $n)) };  On the server side, we have this: $x = H(\text{salt|password}) \\ S = A^b g^{xub} = A^b(g^{ub})^x \\ M = \text{HMAC-SHA256}(\text{SHA256}(S), \text{salt})$ The choice of salt,$b$and$u$is in the sake of simplyfing calculations server-side, I hope I did not miss other more evident values to this regard: $\text{salt} = \text{''} \Rightarrow x = H(\text{password}) \\ b = 1, u = 1 \Rightarrow B = g^b = g, S = Ag^x \\ M = \text{HMAC-SHA256}(\text{SHA256}(S), \text{''})$ Hence weâ€™re sending an empty salt and$B = g$back to the client, hoping they have no specific check to smell rotten fish! This eventually requires us to calculate one exponetiation and one multiplication only. I canâ€™t see space for calculating rainbow tables here, because the use of HMAC would basically require that we calculate it for every possible key$K\$ (taken from a SHA-256), which is definitely too much. I might be missing something here too.

Stay safe and secure!

Comments? Octodon, , GitHub, Reddit, or drop me a line!