return0 has asked for the wisdom of the Perl Monks concerning the following question:
Hello monks! :) I am currently working on a WPA implementation in Perl, - just as a way for me to learn about not only the protocol, but Perl too, because that's what life's all about!
Anyways, I have read loads of documentation and source code, including that from Aircrack-NG. Across the board, the docs don't really match up really and are very vague, at best. So this is what I came up with. I used Net::Pcap with WiFi to capture a perfect EAPOL and beacon with which I passed to Aircrack-NG with a word list and my password inside that list. For those unfamiliar with aircrack-ng, it produces the PTK transient Key, EAPOL, and PMK. I can do the PMK, that's incredibly easy, and you will see from my simple code below. The PTK part is where I am having trouble it seems. I have hard-coded the Anonce, Snonce, AP-MAC (BSSID), and Station MAC into the code as well.
The way it seems to work in practice is that the PMK is used to create the PTK, using a function like so (taken directly from Aircrack-NG): /* pre-compute the key expansion buffer */
memcpy( pke, "Pairwise key expansion", 23 );
if( memcmp( ap->wpa.stmac, ap->bssid, 6 ) < 0 ) {
memcpy( pke + 23, ap->wpa.stmac, 6 );
memcpy( pke + 29, ap->bssid, 6 );
} else {
memcpy( pke + 23, ap->bssid, 6 );
memcpy( pke + 29, ap->wpa.stmac, 6 );
}
if( memcmp( ap->wpa.snonce, ap->wpa.anonce, 32 ) < 0 ) {
memcpy( pke + 35, ap->wpa.snonce, 32 );
memcpy( pke + 67, ap->wpa.anonce, 32 );
} else {
memcpy( pke + 35, ap->wpa.anonce, 32 );
memcpy( pke + 67, ap->wpa.snonce, 32 );
}
// then to get the PTK, they used the string above, like so:
/* compute the pairwise transient key and the frame MIC */
for (i = 0; i < 4; i++)
{
pke[99] = i;
HMAC(EVP_sha1(), pmk[j], 32, pke, 100, ptk[j] + i * 20
+, NULL);
/*
unsigned char *HMAC(const EVP_MD *evp_md, const vo
+id *key,
int key_len, const unsigned char *d, int n,
unsigned char *md, unsigned int *md_len);
*/
}
I have placed in comments a definition of that simple HMAC() function. I tried to make my code mimic, as closely as I could, this function (and as I said, I have hard coded my PMK (which I know is correct)) but simply fall short and cannot replicate the correct PTK given to me from Aircrack-NG or CowPatty :( Below is my code:
#!/usr/bin/perl -w
use strict;
use Crypt::PBKDF2;
use Digest::HMAC_SHA1 qw(hmac_sha1 hmac_sha1_hex);
use List::Util qw( min max );
my $usage = "Usage: perl wpa2hex <ESSID> <PASSPHRASE>\n";
my $essid = shift || die $usage;
my $passwd = shift || die $usage; # pack the next few registers up
my $smac = pack("H*","489d2477179a"); # station MAC (RIM)
my $amac = pack("H*","001dd0f694b0"); # AP MAC (Arris)
my $snonce = pack("H*","143fbb4333341f36e17667f88aa02c5230ab82c508cc4b
+d5947dd7e50475ad36");
my $anonce = pack("H*","87f2718bad169e4987c94255395e054bcaf77c8d791698
+bf03dc85ed3c90832a");
my $pbkdf2 = Crypt::PBKDF2->new(
hash_class => 'HMACSHA1', # HMAC-SHA1
hash_args => {
sha_size => 512,
},iterations => 4096,
salt_len => length($essid),
output_len => 32
);
my $pmk = uc($pbkdf2->PBKDF2($essid, $passwd));
my $pbkdf2b = Crypt::PBKDF2->new(
hash_class => 'HMACSHA1', # HMAC-SHA1
hash_args => {
sha_size => 512,
},iterations => 4096,
salt_len => length($pmk),
output_len => 20
);
print "Master Key: ",uc(unpack("H*",$pmk)),"\n";
prf512();
sub prf512{
my $a = "Pairwise key expansion"; # application-specific data
my $b = $amac.$smac.$snonce.$anonce;
my $r = "";
for(my $i=0;$i<4;$i++){
my $data = $a."\x00".$b.$i;
$r .= $pbkdf2b->PBKDF2($pmk,$data);
}
print "Transient Key: ",uc(unpack("H*",$r)),"\n";
return;
}
I simply used Wikipedia and this site: http goo.gl UVXhGi as a reference. That site, however states that you need to use a "0 byte" between the "Pairwise key expansion" string and the first MAC address in the data passed to the function. Aircrack-NG's source code, as you see above, does not! Anyways, I am not trying to re-invent the wheel here, I am just a student of 802.11, Perl, C and life.
If anyone has any idea as to where I am wrong, please, the help would be greatly appreciated. Thank you in advance, and peace be with you monks <3
Re: HMAC_SHA1 Implementation for WPA
by cord-bin (Friar) on Jun 23, 2014 at 09:43 UTC
|
Hi,
The IEEE 802.11i-2004 says:
The PTK is generated by concatenating the following attributes: PMK, A
+P nonce (ANonce), STA nonce (SNonce), AP MAC address, and STA MAC add
+ress.
You should better check this as the order is important.
This isn't exactly what you are doing here :
my $a = "Pairwise key expansion"; # application-specific data
my $b = $amac.$smac.$snonce.$anonce;
my $r = "";
for(my $i=0;$i<4;$i++){
my $data = $a."\x00".$b.$i;
The product is then put through PBKDF2-SHA1 as the cryptographic hash function.
The PBKDF2 key derivation function has five input parameters:
DK = PBKDF2(PRF, Password, Salt, c, dkLen)
where:
PRF is a pseudorandom function of two parameters with output length hLen (e.g. a keyed HMAC)
Password is the master password from which a derived key is generated
Salt is a cryptographic salt
c is the number of iterations desired
dkLen is the desired length of the derived key
DK is the generated derived key
For more on pbkdf2 used in perl check this link PBKDF2 crypt
Here's some code used to do this pbkdf2.pl | [reply] [d/l] [select] |
|
Hi, there actually have been quite a few amendments since 2004, thankfully. You may want to look into a more updated document. As far as "five input parameters" goes, can you not see my 5 parameters? Also, I know that the order is important, as all documentation on building the string data to send to the HMAC-SHA1 uses min() max() functions in its concatentation syntax. This is why I have them hard-coded at the moment.
In a similar way, the computation of the pairwise temporal keys is wri
+tten:
PRF-512(PMK, "Pairwise key expansion", MAC1||MAC2||Nonce1||Nonce2)
Here MAC1 and MAC2 are the MAC addresses of the two devices where MAC1
+ is the smaller (numerically) and MAC2 is the larger of the two addre
+sses. Similarly, Nonce1 is the smallest value between ANonce and SNon
+ce, while Nonce2 is the largest of the two values.
During my more recent research however, I did find a Python implementation which shows similar difficulties, and might shed some insight on your documentation problem - as he did successfully calculate the PTK in Python in a *very* similar way. http-stackoverflow.com-questions-12018920/ Maybe this is a key, but I do use the pack() function, which seems to be the same. I am going to read the .cap file directly using Net::Pcap now and check if the "string" is somehow causing the issue.
Thanks. | [reply] [d/l] |
|
I have added code into aircrack-ng.c from the latest sauce which writes the pke[] array to a file as so:
FILE *pkef;
pkef=fopen("pke.txt", "a");
for(j=0; j<nparallel; ++j)
{
/* compute the pairwise transient key and the
+frame MIC */
for (i = 0; i < 4; i++)
{
pke[99] = i;
HMAC(EVP_sha1(), pmk[j], 32, pke, 100,
+ ptk[j] + i * 20, NULL);
int kk = 0;
fprintf(pkef,"==> ");
for(kk = 0;kk<=99;kk++){
fprintf(pkef, "%i ",pke[kk]);
}
fprintf(pkef,"\n---------------------\
+n");
}
After which I converted it to hex, and the output was exactly that which it should be:
"Pairwise key expansion" . 0x00 . $mac1 . $mac2 . $nonce1 . $nonce2 . 0x00; # where $mac1 < $mac2 && $nonce1 < $nonce2. Now, I used this hex in Perl and tried hmac_sha1 and even PBKDF2 again, to no avail (using the pre-computed PMK as the key just like Aircrack-ng). I have tried 1 and 4096 passes, I know I am using CCMP-WPA2, I know what cowpatty and aircrack-ng produce, i have also tried reading the raw bytes directly from Net::Pcap but I cannot seem to get the correct first 128 bits of the PTK.. I bet it has to do with not being able to replicate that danged HMAC() C function properly. There's a description of it in the openSSL documentation as:
unsigned char *HMAC(const EVP_MD *evp_md, const void *key,
int key_len, const unsigned char *d, int n,
unsigned char *md, unsigned int *md_len);
Could it simply not be possible? Thanks monks! :) | [reply] [d/l] [select] |
|
Okay, so I feel like I am getting a bit closer. I got the Python program from the link (http-stackoverflow.com-questions-12018920) to work, even though I don't know python, and watched exactly what it was doing.
import hmac
import hashlib
from hashlib import sha1
import binascii
import sys
A = "Pairwise key expansion"
APmac = binascii.a2b_hex("001dd0f694b0")
Clientmac = binascii.a2b_hex("489d2477179a")
ANonce = binascii.a2b_hex("87f2718bad169e4987c94255395e054bcaf77c
+8d791698bf03dc85ed3c90832a")
SNonce = binascii.a2b_hex("143fbb4333341f36e17667f88aa02c5230ab82
+c508cc4bd5947dd7e50475ad36")
B = min(APmac,Clientmac)+max(APmac,Clientmac)+min(ANonce,SNo
+nce)+max(ANonce,SNonce)
def customPRF512(key,A,B):
blen = 64
i = 0
R = ''
while i<=((blen*8+159)/160):
hmacsha1 = hmac.new(key,A+chr(0x00)+B+chr(i),sha1)
i+=1
R = R+hmacsha1.digest()
print "R: ",binascii.b2a_hex(hmacsha1.digest()),"\n"
return R[:blen]
pmk = binascii.a2b_hex("9051ba43660caec7a909fbbe6b91e4685f1457b5a2e236
+60d728afbd2c7abfba")
ptk = customPRF512(pmk,A,B)
print "pmk:\t\t",binascii.b2a_hex(pmk),"\n"
print "ptk:\t\t",binascii.b2a_hex(ptk[0:16]),"\n"
print "A: ",binascii.b2a_hex(A),"\n"
print "CHR(0x00): ",chr(0x00),"\n"
print "B: ",binascii.b2a_hex(B),"\n"
print "key: ",binascii.b2a_hex(pmk),"\n"
print "CHR(0): ",chr(0),"\n"
i = 0
string = A+chr(0x00)+B+chr(i)
print "STRING: ",binascii.b2a_hex(string);
It produced the correct PTK from my PMK using the hmac_sha1 class. You can see it from "directly from python loop" in the code.
#!/usr/bin/perl -w
use strict;
use Digest::HMAC_SHA1 qw(hmac_sha1 hmac_sha1_hex);
my $pmk = pack("H*","9051ba43660caec7a909fbbe6b91e4685f1457b5a2e23660d
+728afbd2c7abfba");
my $a;
foreach(split("","Pairwise key expansion")){
$a .= sprintf("%x",ord($_));
} # 5061697277697365206b657920657870616e73696f6e OK
my $i = 0x00; # 00 in hex
my $smac = pack("H*","489d2477179a");
my $amac = pack("H*","001dd0f694b0");
my $snonce = pack("H*","143fbb4333341f36e17667f88aa02c5230ab82c508cc4b
+d5947dd7e50475ad36");
my $anonce = pack("H*","87f2718bad169e4987c94255395e054bcaf77c8d791698
+bf03dc85ed3c90832a");
my $b = $amac.$smac.$snonce.$anonce;
# Directly from Python code in for loop (without spaces):
# 5061697277697365206b657920657870616e73696f6e 00 001dd0f694b0
+ 489d2477179a1
# 43fbb4333341f36e17667f88aa02c5230ab82c508cc4bd5947dd7e50475a
+d36
# 87f2718bad169e4987c94255395e054bcaf77c8d791698bf03dc85ed3c90
+832a 00
my $hd = $a.$i.$b.$i;
my $digest = hmac_sha1($hd,$pmk);
print unpack("H*",$digest)."\n"; # according to docs: $digest = hmac_s
+ha1($data, $key);
# does not come out as 9287f887faade9257f5a806309a2bac8956fcbec like
+hmac_sha1 from Python ?
I am almost sure that the pack() fucntion in Perl is similar to his a2b_hex() method as I have altered his Python code to print it for each iteration. As you can see the "Pairwise key expansion" is turned into hex via each character's ascii code, which I did with sprintf and ord. That comes out to:
5061697277697365206b657920657870616e73696f6e
which is correct. Is the pack() function argument wrong? is the hmacsha1 not the same as in Python?
I am so lost right now. :(
Thanks monks! | [reply] [d/l] [select] |
|
I am trying to generate hmac_sha256 PTK for 11w client . Can anybody provide similar function for sha256_prf ?
| [reply] |
|
|
Re: HMAC_SHA1 Implementation for WPA
by return0 (Acolyte) on Jun 28, 2014 at 04:06 UTC
|
$pmk = pack("H*","9051ba43660caec7a909fbbe6b91e4685f1457b5a2e23660d728
+afbd2c7abfba");
$hd = pack("H*","5061697277697365206b657920657870616e73696f6e00001dd0f
+694b0489d2477179a143fbb4333341f36e17667f88aa02c5230ab82c508cc4bd5947d
+d7e50475ad3687f2718bad169e4987c94255395e054bcaf77c8d791698bf03dc85ed3
+c90832a00");
my $digest = hmac_sha1($hd,$pmk);
print unpack("H*",$digest)."\n";
calculating these values is easy. Not sure where I was going so wrong, but the PTK outpout now matches Aircrack-NG and Cowpatty! I'll mark this as solved.
May peace be with you monks! | [reply] [d/l] |
|
|