wpa-handshake with python - hashing difficulties - python

I try to write a Python program which calculates the WPA-handshake, but I have problems with the hashes. For comparison I installed cowpatty (to see where I start beeing wrong).
My PMK-generation works fine, but the PTK-calculation alsways seems to be wrong. I am not sure if I have to format my input (macadresses and noces) or just give them into the function as a string.
I will give you my routerinformation, which is no problem since I just set it up for testing.
My program looks as follows:
import hmac,hashlib,binascii
passPhrase = "10zZz10ZZzZ"
ssid = "Netgear 2/158"
A = "Pairwise key expansion"
APmac = "001e2ae0bdd0"
Clientmac = "cc08e0620bc8"
ANonce = "61c9a3f5cdcdf5fae5fd760836b8008c863aa2317022c7a202434554fb38452b"
SNonce = "60eff10088077f8b03a0e2fc2fc37e1fe1f30f9f7cfbcfb2826f26f3379c4318"
B = min(APmac,Clientmac)+max(APmac,Clientmac)+min(ANonce,SNonce)+max(ANonce,SNonce)
data="0103005ffe010900200000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
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),sha)
i+=1
R = R+hmacsha1.digest()
return R[:blen]
pmk = pbkdf2(passPhrase, ssid, 4096, 32) #no sourcecode, since b2a_p(pmk) output fits to those of cowpatty
ptk = customPRF512(pmk,A,B) #the prf-function fits the pseudocode in the ieee, but does not give me the correct output (like cowpatty does)
# and i have no idea why :(
print b2a_p(pmk),"\n\n\n"
print b2a_p(ptk),"\n\n\n"
mic1 = hmac.new(ptk[0:16],data)
print mic1.hexdigest() #should be the mic-calculation, not sure if this is correct...
the desired outputs (which cowpatty confirmed) are:
PMK is
01b8 09f9 ab2f b5dc 4798 4f52 fb2d 112e
13d8 4ccb 6b86 d4a7 193e c529 9f85 1c48
Calculated PTK for "10zZz10ZZzZ" is
bf49 a95f 0494 f444 2716 2f38 696e f8b6
428b cf8b a3c6 f0d7 245a d314 a14c 0d18
efd6 38aa e653 c908 a7ab c648 0a7f 4068
2479 c970 8aaa abc3 eb7e da28 9d06 d535
Calculated MIC with "10zZz10ZZzZ" is
4528 2522 bc67 07d6 a70a 0317 a3ed 48f0
Maybe someone of you could tell me, why my program simply doesn't work. Do the hmac-functions work correctly? Is my input formatted wrong? Do I have to regard endianess anywhere? Thanks for your time in advance, I would appreciate any help!

Alright, I figured it out by myself... more by desperate testing and some luck, than successful research, which lead to nothing long enough. Instead of using the MAC-adresses and nonces as the strings they were, I had to unhexlify them. I used
a2b_hex() #alternatively unhexlify()
My final code looks somewhat like this, defs excluded:
import hmac,hashlib,binascii
passPhrase="10zZz10ZZzZ"
ssid = "Netgear 2/158"
A = "Pairwise key expansion"
APmac = a2b_hex("001e2ae0bdd0")
Clientmac = a2b_hex("cc08e0620bc8")
ANonce = a2b_hex("61c9a3f5cdcdf5fae5fd760836b8008c863aa2317022c7a202434554fb38452b")
SNonce = a2b_hex("60eff10088077f8b03a0e2fc2fc37e1fe1f30f9f7cfbcfb2826f26f3379c4318")
B = min(APmac,Clientmac)+max(APmac,Clientmac)+min(ANonce,SNonce)+max(ANonce,SNonce)
data = a2b_hex("0103005ffe01090020000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
pmk = pbkdf2(passPhrase, ssid, 4096, 32)
ptk = customPRF512(pmk,A,B)
mic = hmac.new(ptk[0:16],data)
print "desiredpmk:\t","01b809f9ab2fb5dc47984f52fb2d112e13d84ccb6b86d4a7193ec5299f851c48"
print "pmk:\t\t",b2a_hex(pmk),"\n"
print "desired ptk:\t","bf49a95f0494f44427162f38696ef8b6"
print "ptk:\t\t",b2a_hex(ptk[0:16]),"\n"
print "desired mic:\t","45282522bc6707d6a70a0317a3ed48f0"
print "mic:\t\t",mic.hexdigest(),"\n"
So the answers to my questions were: yes, hashfunctions work correctly, yes, input is formatted wrong, no, no endianess-issues.

Thanks for posting. This helped me out, so posting my revisions:
#==========================================================================================
#
# Verify the MIC code in EAPoL Message #2 is valid, or not (WPA2)
#
#==========================================================================================
#
# The home for this code is (so check for updates):
#
# https://www.duckware.com/tech/verify-mic-in-four-way-handshake.py.txt
#
# and this code is fully public, as it was based on/derived from this public code:
#
# https://stackoverflow.com/questions/12018920/wpa-handshake-with-python-hashing-difficulties
#
# 1. PMK: 'Pairwise Master Key' (256-bit) is generated from SSID/PASS in WPA2:
#
# o https://www.wireshark.org/tools/wpa-psk.html (SSID/PASS to PMK)
# o http://anandam.name/pbkdf2/ (Password-Based Key Derivation Function 2)
#
# 2. PRF512: The PRF-512 function is used to compute four 128-bit keys (KCK,KEK,TK1,TK2).
# For details on this function, see:
#
# o http://etutorials.org/Networking/802.11+security.+wi-fi+protected+access+and+802.11i/Part+II+The+Design+of+Wi-Fi+Security/Chapter+10.+WPA+and+RSN+Key+Hierarchy/Computing+the+Temporal+Keys/
#
# 3. KCK: The KCK (first 128 bits of the PTK; see above) are used to generate the MIC:
#
# o https://tldp.org/HOWTO/8021X-HOWTO/intro.html
#
# RUN: Run the code below in an ONLINE Python 2.7 compiler. For example:
#
# o https://repl.it/languages/python
# o https://www.tutorialspoint.com/execute_python_online.php
#
# CUSTOMIZE: How to customize the code below:
#
# 1) PCAP the problematic handshake (TIP: use tcpdump with ether host xx:xx:xx:xx:xx:xx)
# 2) Update SSID/PASS vars below with the known Wi-Fi name/password
# 3) Copy entire Ethernet frames for EAPoL Message #1/#2 into EAPOL1/2 vars below.
# TIP: In Wireshark, right click on Ethernet frame, 'Copy' / '...as a Hex Stream' / paste below
# 4) Use first with a working 4-way handshake (to confirm proper usage; MIC match), then apply
# to non-working 4-way handshake to confirm that the MIC in Message #2 is good/bad.
# 5) The code below, unmodified, results in a MIC found/calculated 'match'
#
# See also:
#
# o https://www.wifi-professionals.com/2019/01/4-way-handshake
# o https://stackoverflow.com/questions/15133797/creating-wpa-message-integrity-code-mic-with-python
# o https://www.shellvoide.com/wifi/understanding-wpa-wpa2-hash-mic-cracking-process-python/
# o https://ww.ins1gn1a.com/understanding-wpa-psk-cracking/
# o https://docs.python.org/3/library/binascii.html
# o https://stackoverflow.com/questions/9020843/how-to-convert-a-mac-number-to-mac-string
#
#==========================================================================================
import hmac,hashlib,binascii
def to_mac(addr): return ':'.join(addr[i:i+2] for i in range(0,len(addr),2))
def PRF_512(key,A,B): return ''.join(hmac.new(key,A+chr(0)+B+chr(i),hashlib.sha1).digest() for i in range(4))[:64]
def a2b(s): return binascii.a2b_hex(s);
def b2a(by): return binascii.b2a_hex(by);
EAPOL1 = a2b("60f189052d94a00460216606888e0203005f02008a00100000000000000001141f7a3ebdc0b51712934bef6e43ea13f80cb460f121f35408aa607046e239980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
EAPOL2 = a2b("a0046021660660f189052d94888e0103007502010a000000000000000000015b46c7165f504c664aed90b78f3b705e02b4b029a67e3189d1632479d7e7a4e6000000000000000000000000000000000000000000000000000000000000000056de18f5efa272a4663560b73c537a65001630140100000fac040100000fac040100000fac028000")
SSID = "your-wifi-ssid"
PASS = "your-wifi-password"
PMK = hashlib.pbkdf2_hmac('sha1', PASS, SSID, 4096, 32)
VER_WPA = 2 # WPA2 means use 'SHA1'
XAUTH = a2b("888E")
if EAPOL1[0:6]==EAPOL2[6:12] and EAPOL2[0:6]==EAPOL1[6:12] and EAPOL1[12:14]==XAUTH and EAPOL1[12:14]==XAUTH:
if ord(EAPOL1[20])%8==VER_WPA and ord(EAPOL2[20])%8==VER_WPA:
R1 = EAPOL1[31:63] # random 1 (AP nonce)
R2 = EAPOL2[31:63] # random 2 (STA nonce)
M1 = EAPOL2[0:6] # MAC 1 (AP MAC)
M2 = EAPOL1[0:6] # MAC 2 (STA MAC)
# Generate KCK, KEK, TK1, TK2 from the PMK (and AP/STA info)
PTK = PRF_512(PMK,"Pairwise key expansion",min(M1,M2)+max(M1,M2)+min(R1,R2)+max(R1,R2))
KCK = PTK[0:16];
# try to validate the MIC in EAPoL message #2 is correct
MICRAW = hmac.new(KCK,EAPOL2[14:95]+a2b("00000000000000000000000000000000")+EAPOL2[111:],hashlib.sha1)
MICFOUND = b2a(EAPOL2[95:111])
MICCALC = MICRAW.hexdigest()[0:32]
print "SSID/PASS: ",SSID,"/",PASS
print "PMK: ",b2a(PMK)
print "AP-MAC: ",to_mac(b2a(M1))
print "STA-MAC: ",to_mac(b2a(M2))
print "AP-NONCE: ",b2a(R1)
print "STA-NONCE: ",b2a(R2)
print "KCK: ",b2a(KCK)
print "MIC-found: ",MICFOUND
print "MIC-calc: ",MICCALC
print "Result: ",("OK: EAPoL message #2 validated" if MICFOUND==MICCALC else "ERROR: MIC does not match")
else:
print "***ERROR: Did not find expected 'WPA2' version in EAPoL messages"
else:
print "***ERROR: Problem validated Ethernet frames. Do EAPOL1 and EAPOL2 both include the Ethernet headers?"

Related

How to filter characters for comparison of strings in Python

i am very new to coding and I am not familiar with python, could you guys give me maybe a small example of how you would solve this problem.
Basically this new device i will be working on has a 2D code(its sort of a barcode kkind of thing) and when i scan the code witha 2D scanner a string like this shows up on my notepad for example: 58183#99AF0M000F9EF3F800
the last 12 characters are the MAC address and the first 5 characters are the order number.
i need to compare that(58183#99AF0M000F9EF3F800) with the MAC address value i get from the XML page.
here is the terminal output for more reference:
####################################################################################################
Current device information:
Order-number: 58184 Software-version: 1.0.0 ( Build : 1 ) Hardware version: 1.00 MAC address: 00:0F:9E:F4:1A:80
desired-Order-number: 58183 desired-Softwareversion: 1.0.0 ( Build : 1 ) desired-hardwareversion: 1.00 pc-praefix: 7A2F7
PASS
PS C:\Users\Aswin\Python Project>
The MAC address from the XML page has looks like this "00:0F:9E:F4:1A:80" and the 2D scancode looks like this "58183#99AF0M000F9EF3F800". how can i take the last 12 characters of this scan code and compare it with the mac address from the XML page to see if they match.
Any example of code blocks would be much appreciated guys.
try:
preflash = urllib.request.urlopen("http://10.10.10.2", timeout=3).getcode()
print("Web page status code:", preflash, "FAIL")
sys.exit(0)
except urllib.error.URLError:
correct = urllib.request.urlopen("http://192.168.100.5", timeout=10).getcode()
print("Web page status code:", correct)
print("IP address: 192.168.100.5 is reachable")
print(100*"#")
# Declare url String
url_str = 'http://192.168.100.2/globals.xml'
# open webpage and read values
xml_str = urllib.request.urlopen(url_str).read()
# Parses XML doc to String for Terminal output
xmldoc = minidom.parseString(xml_str)
# prints the order_number from the xmldoc
order_number = xmldoc.getElementsByTagName('order_number')
ord_nmr = order_number[0].firstChild.nodeValue
# prints the firmware_version from the xmldoc
firmware_version = xmldoc.getElementsByTagName('firmware_version')
frm_ver = firmware_version[0].firstChild.nodeValue
# prints the hardware_version from the xmldoc
hardware_version = xmldoc.getElementsByTagName('hardware_version')
hrd_ver = hardware_version[0].firstChild.nodeValue
v = hrd_ver.split()[-1]
# prints the mac_address from the xmldoc
mac_address = xmldoc.getElementsByTagName('mac_address')
mac_addr = mac_address[0].firstChild.nodeValue
print("Current device information: ")
print("Order-number: ",ord_nmr, "Software-version: ",frm_ver, "Hardware version: ",v, "MAC address: ",mac_addr)
d_ordernum = "58183"
d_hw_version = "1.00"
d_sf_version = "1.0.0 ( Build : 1 )"
pc_praefix = "7A2F7"
print("desired-Order-number: 58183 desired-Softwareversion: 1.0.0 ( Build : 1 ) desired-hardwareversion: 1.00 pc-praefix: 7A2F7")
if d_sf_version == frm_ver:
print("PASS")
else:
print("FAIL")
You could take the string from the scan code and slice it
scan_code_cropped = scancode_string[11:]
This will get you the last 12 characters of the scan code.
Now to get the MAC address in a format to be able to compare it to the scan code, split it on the basis of ":"
list_of_chars = mac_address_string.split(":")
this will get you the character list, which can be concatenated using
mac_address_string_joined = ''.join(list_of_chars)
and finally to compare the two strings
if scan_code_cropped == mac_address_string_joined:
print("Mac address & Scan code matched !")
If needed in a function format, here you go:
def match_scan_with_mac(scancode_string, mac_address_string):
# get the last 12 characters of the scan code
scan_code_cropped = scancode_string[11:]
# get the mac address without the ":"
list_of_chars = mac_address_string.split(":")
mac_address_string_joined = ''.join(list_of_chars)
# compare the MAC address and the scan string
if scan_code_cropped == mac_address_string_joined:
print("Mac address & Scan code matched !")
return True
return False

get next subnet in CIDR format

I have the following:-
start_range = "10.40.0.0/16"
end_range = "10.100.0.0/16"
I have to write a logic to iterate over all possible ranges(with same subnet mask /16) from start to end. For each subnet I will be doing some processing and then continuing to the next one.
I could achieve this in trivial way where I know, I have to increment last network octate(i.e increment 40 to 41 -> 42 -> 43 and so on).
start_subnet = "10.40.0.0/16"
end_subnet = "10.100.0.0/16"
start_val = int(start_subnet.split(".")[1])
end_val = int(end_subnet.split('.')[1])
subnet_split = start_subnet.split(".")
subnet_split[1] = "{}"
subnet_proto = ".".join(subnet_split) # "10.{}.0.0/16"
for i in range(start_val, end_val+1): # iterate from 40 to 100
cur_subnet = subnet_proto.format(i) # "10.40.0.0/16", "10.41.0.0/16" etc
# do processing on cur_subnet
Is there a better(Pythonic) way to get the next subnet(in CIDR format). May be netaddr module has something I am not aware of?
Following usage of netaddr helped me to get the expected result.
from netaddr import IPNetwork
start_range = IPNetwork("10.40.0.0/16")
end_range = IPNetwork("10.45.0.0/16")
allowed_range = []
while start_range<=end_range:
allowed_range.append(start_range)
start_range = start_range.next()
print allowed_range
This would print the following:-
[IPNetwork('10.40.0.0/16'), IPNetwork('10.41.0.0/16'),
IPNetwork('10.42.0.0/16'), IPNetwork('10.43.0.0/16'),
IPNetwork('10.44.0.0/16'), IPNetwork('10.45.0.0/16')]
This might be what you have in mind (not really well thought-through or tested, you've been warned!).
The ipaddress module is from python3 but it has a backport, just run
pip install ipaddress
to get it.
import ipaddress
def subnet_range(start_subnet, end_subnet):
start = ipaddress.ip_network(unicode(start_subnet))
end = ipaddress.ip_network(unicode(end_subnet))
assert start.prefixlen == end.prefixlen
ranges = [
n
for ipaddr in ipaddress.summarize_address_range(
list(start)[0],
list(end)[0])
for n in ipaddr.subnets(new_prefix=start.prefixlen)][:-1]
ranges.append(end)
return ranges
if __name__ == "__main__":
start_subnet = "9.250.0.0/16"
end_subnet = "10.100.0.0/16"
for r in subnet_range(start_subnet, end_subnet):
print r

How to use flow control in pyserial

I need help is using serial port, I have 3DR radio telemetry connected to my python and other side to my windows PC ,I have small python code which continiously writes the data to serial port and reads , reading might not be an issue, or it might be later anyway...
The issue is i am afraid the too many writes might cause some buffer overflow, every time i search the solution is to enable rts/cts flow control, I dont know how to use it ?? what will happen if i set these then pyserial will do what and how can i control my write ?? its really confusing ..
hardware flow ccontol, I am not sure it might work, becuase I have just connected rx tx ground and power to my raspberry pi, even if try to connect the other flow control pins to pi, i am not sure it works or supported by 3dr radio telemetry.. I believe software flow control will be good and simple solution for now.
here is my code ..
for channel in list(self.__channelDict.values()):
# Addition for channel priority later
# We check if the channels in the list is active
if channel.getChannelActive() is True:
# Check if we have reached the max count
if (messageCount >= (self.__NoOfMessagesInUARTStream - 1)) or UARTForceSend:
self.sendUARTStream(UARTCacheBuffer, messageCount, UARTStreamCRC)
# Reset
messageCount = 0
UARTStreamCRC = 0
UARTCacheBuffer.emptyBuffer()
message = channel.RetriveMessage(queueType = 1, raw = True)
# # there is no TX message in this channel
if message is None:
continue # continue with next channel
else:
UARTStreamCRC = binascii.crc32(message, UARTStreamCRC)
UARTCacheBuffer.append(message, raw = True)
messageCount +=1
and the function to write to serial port
def sendUARTStream(self, UARTCacheBuffer, messageCount, UARTStreamCRC):
# retrieve all the data from the buffer and create a stream packet
UARTFrame = None # Used to forward the data
UARTStreamHeader = None
# Create the message header
if messageCount == 0:
# looks like all channels are empty
return 0
else:
messageArray = UARTCacheBuffer.getBuffer()
print(messageArray)
print('messageCount = ' + str(messageCount) + 'crc = ' + str(UARTStreamCRC))
UARTFrame[:self.__UARTStreamHeaderFormat.size] = self.createHeader(messageCount, UARTStreamCRC)
UARTFrame[self.__UARTStreamHeaderFormat.size : self.__UARTStreamHeaderFormat.size + self.__messageFormat * messageCount] = messageArray
# Its time to finally send the data
print('UARTFrame = ##' + str(UARTFrame))
self.__txPort.write(UARTFrame)
return messageCount

Userfriendly way of handling config files in python?

I want to write a program that sends an e-mail to one or more specified recipients when a certain event occurs. For this I need the user to write the parameters for the mail server into a config. Possible values are for example: serveradress, ports, ssl(true/false) and a list of desired recipients.
Whats the user-friendliest/best-practice way to do this?
I could of course use a python file with the correct parameters and the user has to fill it out, but I wouldn't consider this user friendly. I also read about the 'config' module in python, but it seems to me that it's made for creating config files on its own, and not to have users fill the files out themselves.
Are you saying that the fact that the config file would need to be valid Python makes it unfriendly? It seems like having lines in a file like:
server = 'mail.domain.com'
port = 25
...etc would be intuitive enough while still being valid Python. If you don't want the user to have to know that they have to quote strings, though, you might go the YAML route. I use YAML pretty much exclusively for config files and find it very intuitive, and it would also be intuitive for an end user I think (though it requires a third-party module - PyYAML):
server: mail.domain.com
port: 25
Having pyyaml load it is simple:
>>> import yaml
>>> yaml.load("""a: 1
... b: foo
... """)
{'a': 1, 'b': 'foo'}
With a file it's easy too.
>>> with open('myconfig.yaml', 'r') as cfile:
... config = yaml.load(cfile)
...
config now contains all of the parameters.
I doesn't matter technically proficient your users are; you can count on them to screw up editing a text file. (They'll save it in the wrong place. They'll use MS Word to edit a text file. They'll make typos.) I suggest making a gui that validates the input and creates the configuration file in the correct format and location. A simple gui created in Tkinter would probably fit your needs.
I've been using ConfigParser. It's designed to read .ini style files that have:
[section]
option = value
It's quite easy to use and the documentation is pretty easy to read. Basically you just load the whole file into a ConfigParser object:
import ConfigParser
config = ConfigParser.ConfigParser()
config.read('configfile.txt')
Then you can make sure the users haven't messed anything up by checking the options. I do so with a list:
OPTIONS =
['section,option,defaultvalue',
.
.
.
]
for opt in OPTIONS:
section,option,defaultval = opt.split(',')
if not config.has_option(section,option):
print "Missing option %s in section %s" % (option,section)
Getting the values out is easy too.
val = config.get('section','option')
And I also wrote a function that creates a sample config file using that OPTIONS list.
new_config = ConfigParser.ConfigParser()
for opt in OPTIONS:
section,option,defaultval = opt.split(',')
if not new_config.has_section(section):
new_config.add_section(section)
new_config.set(section, option, defaultval)
with open("sample_configfile.txt", 'wb') as newconfigfile:
new_config.write(newconfigfile)
print "Generated file: sample_configfile.txt"
What are the drawbacks of such a solution:
ch = 'serveradress = %s\nport = %s\nssl = %s'
a = raw_input("Enter the server's address : ")
b = 'a'
bla = "\nEnter the port : "
while not all(x.isdigit() for x in b):
b = raw_input(bla)
bla = "Take care: you must enter digits exclusively\n"\
+" Re-enter the port (digits only) : "
c = ''
bla = "\nChoose the ssl option (t or f) : "
while c not in ('t','f'):
c = raw_input(bla)
bla = "Take care: you must type f or t exclusively\n"\
+" Re-choose the ssl option : "
with open('configfile.txt','w') as f:
f.write(ch % (a,b,c))
.
PS
I've read in the jonesy's post that the value in a config file may have to be quoted. If so, and you want the user not to have to write him/her-self the quotes, you simply add
a = a.join('""')
b = b.join('""')
c = c.join('""')
.
EDIT
ch = 'serveradress = %s\nport = %s\nssl = %s'
d = {0:('',
"Enter the server's address : "),
1:("Take care: you must enter digits exclusively",
"Enter the port : "),
2:("Take care: you must type f or t exclusively",
"Choose the ssl option (t or f) : ") }
def func(i,x):
if x is None:
return False
if i==0:
return True
elif i==1:
try:
ess = int(x)
return True
except:
return False
elif i==2:
if x in ('t','f'):
return True
else:
return False
li = len(d)*[None]
L = range(len(d))
while True:
for n in sorted(L):
bla = d[n][1]
val = None
while not func(n,val):
val = raw_input(bla)
bla = '\n '.join(d[n])
li[n] = val.join('""')
decision = ''
disp = "\n====== If you choose to process, =============="\
+"\n the content of the file will be :\n\n" \
+ ch % tuple(li) \
+ "\n==============================================="\
+ "\n\nDo you want to process (type y) or to correct (type c) : "
while decision not in ('y','c'):
decision = raw_input(disp)
disp = "Do you want to process (type y) or to correct (type c) ? : "
if decision=='y':
break
else:
diag = False
while not diag:
vi = '\nWhat lines do you want to correct ?\n'\
+'\n'.join(str(j)+' - '+line for j,line in enumerate((ch % tuple(li)).splitlines()))\
+'\nType numbers of lines belonging to range(0,'+str(len(d))+') separated by spaces) :\n'
to_modify = raw_input(vi)
try:
diag = all(int(entry) in xrange(len(d)) for entry in to_modify.split())
L = [int(entry) for entry in to_modify.split()]
except:
diag = False
with open('configfile.txt','w') as f:
f.write(ch % tuple(li))
print '-*- Recording of the config file : done -*-'

XMODEM for python

I am writing a program that requires the use of XMODEM to transfer data from a sensor device. I'd like to avoid having to write my own XMODEM code, so I was wondering if anyone knew if there was a python XMODEM module available anywhere?
def xmodem_send(serial, file):
t, anim = 0, '|/-\\'
serial.setTimeout(1)
while 1:
if serial.read(1) != NAK:
t = t + 1
print anim[t%len(anim)],'\r',
if t == 60 : return False
else:
break
p = 1
s = file.read(128)
while s:
s = s + '\xFF'*(128 - len(s))
chk = 0
for c in s:
chk+=ord(c)
while 1:
serial.write(SOH)
serial.write(chr(p))
serial.write(chr(255 - p))
serial.write(s)
serial.write(chr(chk%256))
serial.flush()
answer = serial.read(1)
if answer == NAK: continue
if answer == ACK: break
return False
s = file.read(128)
p = (p + 1)%256
print '.',
serial.write(EOT)
return True
There is XMODEM module on PyPi. It handles both sending and receiving of data with XModem. Below is sample of its usage:
import serial
try:
from cStringIO import StringIO
except:
from StringIO import StringIO
from xmodem import XMODEM, NAK
from time import sleep
def readUntil(char = None):
def serialPortReader():
while True:
tmp = port.read(1)
if not tmp or (char and char == tmp):
break
yield tmp
return ''.join(serialPortReader())
def getc(size, timeout=1):
return port.read(size)
def putc(data, timeout=1):
port.write(data)
sleep(0.001) # give device time to prepare new buffer and start sending it
port = serial.Serial(port='COM5',parity=serial.PARITY_NONE,bytesize=serial.EIGHTBITS,stopbits=serial.STOPBITS_ONE,timeout=0,xonxoff=0,rtscts=0,dsrdtr=0,baudrate=115200)
port.write("command that initiates xmodem send from device\r\n")
sleep(0.02) # give device time to handle command and start sending response
readUntil(NAK)
buffer = StringIO()
XMODEM(getc, putc).recv(buffer, crc_mode = 0, quiet = 1)
contents = buffer.getvalue()
buffer.close()
readUntil()
I think you’re stuck with rolling your own.
You might be able to use sz, which implements X/Y/ZMODEM. You could call out to the binary, or port the necessary code to Python.
Here is a link to XMODEM documentation that will be useful if you have to write your own. It has detailed description of the original XMODEM, XMODEM-CRC and XMODEM-1K.
You might also find this c-code of interest.
You can try using SWIG to create Python bindings for the C libraries linked above (or any other C/C++ libraries you find online). That will allow you to use the same C API directly from Python.
The actual implementation will of course still be in C/C++, since SWIG merely creates bindings to the functions of interest.
There is a python module that you can use -> https://pypi.python.org/pypi/xmodem
You can see the transfer protocol in http://pythonhosted.org//xmodem/xmodem.html

Categories

Resources