Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

convert c enum bitfield to python

Tags:

python

enums

Looking at kismet's source code in packet_ieee80211.h is the section

enum crypt_type {
    crypt_none = 0,
crypt_unknown = 1,
crypt_wep = (1 << 1),
crypt_layer3 = (1 << 2),
// Derived from WPA headers
crypt_wep40 = (1 << 3),
crypt_wep104 = (1 << 4),
crypt_tkip = (1 << 5),
crypt_wpa = (1 << 6),
crypt_psk = (1 << 7),
crypt_aes_ocb = (1 << 8),
crypt_aes_ccm = (1 << 9),
//WPA Migration Mode
crypt_wpa_migmode = (1 << 19),
// Derived from data traffic
crypt_leap = (1 << 10),
crypt_ttls = (1 << 11),
crypt_tls = (1 << 12),
crypt_peap = (1 << 13),
crypt_isakmp = (1 << 14),
    crypt_pptp = (1 << 15),
crypt_fortress = (1 << 16),
crypt_keyguard = (1 << 17),
crypt_unknown_nonwep = (1 << 18),
};

I understand this as shifting bits but that's about it. Suppose I have the int 706, how do I break this number up into the cryptset as defined above i.e. how can I extract which crypts are used give 706 especially ported to python

Thanks

like image 789
WraithWireless Avatar asked Apr 27 '14 15:04

WraithWireless


2 Answers

So, what you have to understand here is that this enum is defining a series of bitmasks. In this case, each one of these enum values, in binary, contains one and only one 1. For example,

crypt_wep = (1 << 1) = 0b10
crypt_wpa = (1 << 6) = 0b1000000

And so on. Using the bitshift operator is just an easy way of representing 'I want the nth+1 binary digit to be the flag'

This lets us bitwise or a bunch of these values together and get a magic number that describes a combination of these crypt values uniquely as bit flags. To test if a magic number contains a value, we can simply bitwise and it with the value we wish to test

magic_number = crypt_wep | crypt_wpa
has_wep      = (magic_number & crypt_wep) == crypt_wep
has_wpa      = (magic_number & crypt_wpa) == crypt_wpa

has_wep and has_wpa will be true if and only if magic_number contained those bit flags.

So lets look at the number 706, which in binary is 0b1011000010. We can look at this and immediately see that it must have been built with crypt_wep, crypt_wpa, crypt_psk and crypt_aes_ccm, because the correct bits are set for those values.

So how to port to python? Well, python has enums just like C/C++ does if you're in python 3.4 or later. So you can simply create the same enum table in python, and apply the same bitwise tests to determine what your magic number represents. If you're on a version of python that doesn't have enums, you can simply define a class with some static constants in it to get the same effect (and build in a method to test for the set of crypts a magic number contains). Such a class could look like this:

class CryptKeys(object):
    crypt_masks = {
                    'crypt_unknown':1,
                     ....
                    'crypt_unknown_nonwep': (1 << 18)
                   }
    @classmethod
    def find_crypts(cls, magic_number):
        if magic_number == 0:
            return ['crypt_none']
        else:
            return [name for name, mask in cls.crypt_masks.items() if magic_number & mask == mask]
like image 68
aruisdante Avatar answered Oct 17 '22 10:10

aruisdante


Aruisdante's answer is very good, I just wanted to expand on the answer if you have to use a Python previous to 3.4, as there does exist a backport on PyPI:

from enum import IntEnum

class Crypt(IntEnum):
    none = 0
    unknown = 1
    wep = (1 << 1)
    layer3 = (1 << 2)
    # Derived from WPA headers
    wep40 = (1 << 3)
    wep104 = (1 << 4)
    tkip = (1 << 5)
    wpa = (1 << 6)
    psk = (1 << 7)
    aes_ocb = (1 << 8)
    aes_ccm = (1 << 9)
    # WPA Migration Mode
    wpa_migmode = (1 << 19)
    # Derived from data traffic
    leap = (1 << 10)
    ttls = (1 << 11)
    tls = (1 << 12)
    peap = (1 << 13)
    isakmp = (1 << 14)
    pptp = (1 << 15)
    fortress = (1 << 16)
    keyguard = (1 << 17)
    unknown_nonwep = (1 << 18)

    @classmethod
    def find_crypts(cls, magic_number):
        crypts = []
        for mask in cls:
            if magic_number & mask == mask:
                crypts.append(mask)
        if len(crypts) > 1:
            # remove false positive of none
            crypts = crypts[1:]
        return crypts

print Crypt.find_crypts(0)
[<Crypt.none: 0>]
print Crypt.find_crypts(706)
[<Crypt.wep: 2>, <Crypt.wpa: 64>, <Crypt.psk: 128>, <Crypt.aes_ccm: 512>]
like image 3
Ethan Furman Avatar answered Oct 17 '22 11:10

Ethan Furman