def decrypt_frame(self, encrypted_frame): """ Decrypt a frame. """ id = encrypted_frame[0:HEADER_ID_SIZE] cipher_text = encrypted_frame[HEADER_ID_SIZE:] # check if 'id; is correct for link # note - AD maintains two links, for simplicity this is not shown # AD also supports a pairing discovery mode. In pairing # discovery the frame is not rejected, but used to discover # the peer's UDI and retrieve the UDS from the Key Dispenser (KD) # This is not yet shown and code only represents Operational link if id != self.udi[0:HEADER_ID_SIZE]: # reject the frame raise Exception("Wrong id on frame, not for this device") # decrypt the frame using the 'id' as an authenticated attribute # the SIV mode extracts the siv and raises an exception on # any integrity check failure plain_text = siv_decrypt(self._uds, cipher_text, [id]) # parse fields in decrypted frame rt = plain_text[ 0:REPLAY_VALUE_SIZE] # peer transmitted replay protection value rr = plain_text[REPLAY_VALUE_SIZE:2 * REPLAY_VALUE_SIZE] # echo of self's value message_data = plain_text[2 * REPLAY_VALUE_SIZE:] # validate if replay values are correct if rr == self.my_replay_value: # correct echoed 'rr' for syncronized operational link # if this is the first time recived change state if self.my_replay_value_state == NEW: self.my_replay_value_state = ECHOED if rt == self.peer_replay_value: # expected peer value - normal operation return message_data else: # change in peer replay value # adopt new value and echo on subsequent transmissions self.peer_replay_value = rt return message_data elif rr == ZERO_REPLAY_FIELD: # peer has reset so adopt the new rt value self.peer_replay_value = rt # creat new local replay value self.my_replay_value = os.urandom(REPLAY_VALUE_SIZE) # reset the transmitted frame count (actually count for a replay value) self.transmitted_frame_count = 0 # note any message_data sent in a reset frame (rr==0's) is subject to a # replay attack and should be restricted to static link setup information return message_data else: # possible replay attack # on replay errors the frame is dropped by rasing an exception raise Exception("Bad peer replay value - likely replay attack")
def test_Edges(self): """ Test edge conditions - zero lengths """ # zero length plian text, multiple ad key = '7f7e7d7c7b7a79787776757473727170404142434445464748494a4b4c4d4e4f'.decode('hex') pt = '' ad1 = 'abcd'.decode('hex') ad2 = 'beef'.decode('hex') nonce = '09f911029d74e35bd84156c5635688c0'.decode('hex') ad = [ad1, ad2, nonce] iv_ct = siv_encrypt(key, pt, ad) pt2 = siv_decrypt(key, iv_ct, ad) self.assertEqual( pt, pt2 ) # no ad iv_ct = siv_encrypt(key, 'a', []) pt2 = siv_decrypt(key, iv_ct, []) self.assertEqual( 'a', pt2 )
def test_Edges(self): """ Test edge conditions - zero lengths """ # zero length plian text, multiple ad key = "7f7e7d7c7b7a79787776757473727170404142434445464748494a4b4c4d4e4f".decode("hex") pt = "" ad1 = "abcd".decode("hex") ad2 = "beef".decode("hex") nonce = "09f911029d74e35bd84156c5635688c0".decode("hex") ad = [ad1, ad2, nonce] iv_ct = siv_encrypt(key, pt, ad) pt2 = siv_decrypt(key, iv_ct, ad) self.assertEqual(pt, pt2) # no ad iv_ct = siv_encrypt(key, "a", []) pt2 = siv_decrypt(key, iv_ct, []) self.assertEqual("a", pt2)
def test_A1(self): """ A.1. Deterministic Authenticated Encryption Example """ key = 'fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'.decode('hex') ad = '101112131415161718191a1b1c1d1e1f2021222324252627'.decode('hex') pt = '112233445566778899aabbccddee'.decode('hex') iv_ct = siv_encrypt(key, pt, [ad]) known_ct = '85632d07c6e8f37f950acd320a2ecc9340c02b9690c4dc04daef7f6afe5c'.decode('hex') self.assertEqual( iv_ct, known_ct ) pt2 = siv_decrypt(key, iv_ct, [ad]) self.assertEqual( pt, pt2 )
def decrypt_frame(self, encrypted_frame): """ Decrypt a frame. """ id = encrypted_frame[0:HEADER_ID_SIZE] cipher_text = encrypted_frame[HEADER_ID_SIZE:] # check if 'id; is correct for link # note - AD maintains two links, for simplicity this is not shown # AD also supports a pairing discovery mode. In pairing # discovery the frame is not rejected, but used to discover # the peer's UDI and retrieve the UDS from the Key Dispenser (KD) # This is not yet shown and code only represents Operational link if id != self.udi[0:HEADER_ID_SIZE]: # reject the frame raise Exception("Wrong id on frame, not for this device") # decrypt the frame using the 'id' as an authenticated attribute # the SIV mode extracts the siv and raises an exception on # any integrity check failure plain_text = siv_decrypt(self._uds, cipher_text, [id]) # parse fields in decrypted frame rt = unpack("I", plain_text[0:REPLAY_VALUE_SIZE] ) # peer transmitted replay protection value rr = unpack("I", plain_text[REPLAY_VALUE_SIZE:2 * REPLAY_VALUE_SIZE]) # echo of self's value message_data = plain_text[2 * REPLAY_VALUE_SIZE:] # validate if replay values are correct using a window for recived replay value # the recived if self.my_replay_value >= rr >= self.my_replay_value - REPLAY_CTR_WINDOW: # acceptable echoed 'rr' within window range (peer may have not recived frames) # check if rt is within it's window (frames may have been lost from peer) if self.peer_replay_value <= rt <= self.peer_replay_value + REPLAY_CTR_WINDOW: return message_data else: # peer replay values outside of allowed window # possible replay attack or long burst of lost data, reset replay self.reset() return '' # discard message_data and quitely return null frame data elif rr == ZERO_REPLAY_FIELD: # peer has reset so adopt the new rt value self.peer_replay_value = rt # note any message_data sent in a reset frame (rr==0's) is subject to a # replay attack and should be restricted to static link setup information return message_data else: # possible replay attack or long burst of peer losing frames # on replay errors the frame is dropped by rasing an exception raise Exception("Bad peer replay value - likely replay attack")
def test_A2(self): """ A.2. Nonce-based Authenticated Encryption Example """ key = '7f7e7d7c7b7a79787776757473727170404142434445464748494a4b4c4d4e4f'.decode('hex') ad1 = '00112233445566778899aabbccddeeffdeaddadadeaddadaffeeddccbbaa99887766554433221100'.decode('hex') ad2 = '102030405060708090a0'.decode('hex') nonce = '09f911029d74e35bd84156c5635688c0'.decode('hex') ad = [ad1, ad2, nonce] pt = '7468697320697320736f6d6520706c61696e7465787420746f20656e6372797074207573696e67205349562d414553'.decode('hex') iv_ct = siv_encrypt(key, pt, ad) known_ct = '7bdb6e3b432667eb06f4d14bff2fbd0fcb900f2fddbe404326601965c889bf17dba77ceb094fa663b7a3f748ba8af829ea64ad544a272e9c485b62a3fd5c0d'.decode('hex') self.assertEqual( iv_ct, known_ct ) pt2 = siv_decrypt(key, iv_ct, ad) self.assertEqual( pt, pt2 )