diff --git a/doorcontrol/hid/Credential.py b/doorcontrol/hid/Credential.py index 5bc3248..2ba1af9 100644 --- a/doorcontrol/hid/Credential.py +++ b/doorcontrol/hid/Credential.py @@ -25,15 +25,23 @@ class InvalidParity(InvalidHexCode): class Credential: bits: bitstring.Bits + @staticmethod + def even_parity(bits: bitstring.Bits) -> bool: + return bool(bits[7:19].count(1) % 2) + + @staticmethod + def odd_parity(bits: bitstring.Bits) -> bool: + return (bits[19:31].count(1) % 2) == 0 + @classmethod - def from_code(cls, facility=int, card_number=int) -> "Credential": + def from_code(cls, facility: int, card_number: int) -> "Credential": bits = bitstring.pack( "0b000000, 0b0, uint:8=facility, uint:16=card_number, 0b0", facility=facility, card_number=card_number, ) - bits[6] = bits[7:19].count(1) % 2 # even parity - bits[31] = bits[19:31].count(0) % 2 # odd parity + bits[6] = cls.even_parity(bits) + bits[31] = cls.odd_parity(bits) return cls(bitstring.Bits(bits)) @classmethod @@ -42,9 +50,9 @@ class Credential: if bits[:6].any(1): raise Not26Bit(bits) - if bits[6] != bits[7:19].count(1) % 2: + if bits[6] != cls.even_parity(bits): raise InvalidParity(bits, "even") - if bits[31] != (bits[19:31].count(0) % 2): + if bits[31] != (cls.odd_parity(bits)): raise InvalidParity(bits, "odd") return cls(bits) diff --git a/doorcontrol/hid/tests/test_Credential.py b/doorcontrol/hid/tests/test_Credential.py index 7e9b240..fe9bde7 100644 --- a/doorcontrol/hid/tests/test_Credential.py +++ b/doorcontrol/hid/tests/test_Credential.py @@ -18,3 +18,13 @@ class CredentialTestCase(TestCase): cred = Credential.from_code(facility_code, card_number) hex_cred = Credential.from_hex(cred.hex) self.assertEqual(cred, hex_cred) + + def test_example_numbers(self): + examples = [ + (100, 12345, "02C86073"), + ] + + for facility_code, card_number, hex_number in examples: + code_cred = Credential.from_code(facility_code, card_number) + hex_cred = Credential.from_hex(hex_number) + self.assertEqual(code_cred, hex_cred)