def runTest(self): key = b"0" * 16 data = b"\x00\x01\x02" def get_mv_ro(data): return memoryview(data) def get_mv_rw(data): return memoryview(bytearray(data)) for get_mv in (get_mv_ro, get_mv_rw): # Data and key can be a memoryview (during initialization) key_mv = get_mv(key) data_mv = get_mv(data) h1 = CMAC.new(key, data, ciphermod=AES) h2 = CMAC.new(key_mv, data_mv, ciphermod=AES) if not data_mv.readonly: key_mv[:1] = b'\xFF' data_mv[:1] = b'\xFF' self.assertEqual(h1.digest(), h2.digest()) # Data can be a memoryview (during operation) data_mv = get_mv(data) h1 = CMAC.new(key, ciphermod=AES) h2 = CMAC.new(key, ciphermod=AES) h1.update(data) h2.update(data_mv) if not data_mv.readonly: data_mv[:1] = b'\xFF' self.assertEqual(h1.digest(), h2.digest())
def runTest(self): key = b"0" * 16 data = b"\x00\x01\x02" # Data and key can be a bytearray (during initialization) key_ba = bytearray(key) data_ba = bytearray(data) h1 = CMAC.new(key, data, ciphermod=AES) h2 = CMAC.new(key_ba, data_ba, ciphermod=AES) key_ba[:1] = b'\xFF' data_ba[:1] = b'\xFF' self.assertEqual(h1.digest(), h2.digest()) # Data can be a bytearray (during operation) key_ba = bytearray(key) data_ba = bytearray(data) h1 = CMAC.new(key, ciphermod=AES) h2 = CMAC.new(key, ciphermod=AES) h1.update(data) h2.update(data_ba) data_ba[:1] = b'\xFF' self.assertEqual(h1.digest(), h2.digest())
def get_content_block(buff): global cmac_keyx hash=hashlib.sha256(buff).digest() key = int16bytes(normalkey(cmac_keyx, keyy)) cipher = CMAC.new(key, ciphermod=AES) result = cipher.update(hash) return result.digest() + b''.join(bytechr(random.randint(0,255)) for _ in range(16))
def q2_siv_mode_dec(enc_key, mac_key, ciphertext, associated_data): """Question 2 (part 2): Synthetic Initialization Vector (SIV) Authenticated Encryption Your Task: Similar to the first part of this question, your function should decrypt the output produced by the function in the first part and return the plaintext if the tag is valid, and return ERROR otherwise. Args: enc_key (str): 16-bytes hex-encoded key to be used for AES mac_key (str): 16-bytes hex-encoded key to be used for CMAC ciphertext (str): arbitrary-length hec-encoded ciphertext (same format as the output of q2_siv_mode_enc) associated_data (str): arbitrary-length hex-encoded data to be authenticated, but not encrypted Output: ret (str): ASCII-encoded, plaintext (or 'ERROR') Test vectors: Use the same test case provided in part 1 of this question. """ backend = default_backend() tag = ciphertext[:32] cipherMessage = ciphertext[32:] cipher = Cipher(algorithms.AES(bytes.fromhex(enc_key)), modes.CTR(bytes.fromhex(tag)), backend) decryptor = cipher.decryptor() message = decryptor.update(bytes.fromhex(cipherMessage)) cmac_input = bytes.fromhex(associated_data) + message cobj = CMAC.new(bytes.fromhex(mac_key), ciphermod=AES) cobj.update(cmac_input) ComputedTag = cobj.hexdigest() if (tag != ComputedTag): return 'ERROR' else: return message.decode('ascii')
def getContentKey(self, license_request_data: bytes, license_response_data: bytes): licenseMessage = license_protocol_pb2.License() requestMessage = license_protocol_pb2.SignedMessage() responseMessage = license_protocol_pb2.SignedMessage() requestMessage.ParseFromString(license_request_data) responseMessage.ParseFromString(license_response_data) oaep_key = RSA.importKey(self.private_key) cipher = PKCS1_OAEP.new(oaep_key) cmac_key = cipher.decrypt(responseMessage.session_key) _cipher = CMAC.new(cmac_key, ciphermod=AES) _auth_key = b'\x01ENCRYPTION\x00' + requestMessage.msg + b"\x00\x00\x00\x80" enc_cmac_key = _cipher.update(_auth_key).digest() licenseMessage.ParseFromString(responseMessage.msg) global KEY_ARRAY KEY_ARRAY = [] for key in licenseMessage.key: cryptos = AES.new(enc_cmac_key, AES.MODE_CBC, iv=key.iv[0:16]) dkey = cryptos.decrypt(key.key[0:16]) # print("KID:", binascii.b2a_hex(key.id).decode('utf-8'), "KEY:",binascii.b2a_hex(dkey).decode('utf-8')) KEY_ARRAY.append("%s:%s" % (binascii.b2a_hex(key.id).decode('utf-8'), binascii.b2a_hex(dkey).decode('utf-8'))) KEY_ARRAY.remove(KEY_ARRAY[0]) for item in KEY_ARRAY: print("[info][Found KEY] %s" % item)
def update(self, item): """Pass the next component of the vector. The maximum number of components you can pass is equal to the block length of the cipher (in bits) minus 1. :Parameters: item : byte string The next component of the vector. :Raise TypeError: when the limit on the number of components has been reached. :Raise ValueError: when the component is empty """ if not item: raise ValueError("A component cannot be empty") if self._n_updates==0: raise TypeError("Too many components passed to S2V") self._n_updates -= 1 mac = CMAC.new(self._key, msg=self._last_string, ciphermod=self._ciphermod, cipher_params=self._cipher_params) self._cache = strxor(self._double(self._cache), mac.digest()) self._last_string = item
def update(self, item): """Pass the next component of the vector. The maximum number of components you can pass is equal to the block length of the cipher (in bits) minus 1. :Parameters: item : byte string The next component of the vector. :Raise TypeError: when the limit on the number of components has been reached. :Raise ValueError: when the component is empty """ if not item: raise ValueError("A component cannot be empty") if self._n_updates == 0: raise TypeError("Too many components passed to S2V") self._n_updates -= 1 mac = CMAC.new(self._key, msg=self._last_string, ciphermod=self._ciphermod, cipher_params=self._cipher_params) self._cache = strxor(self._double(self._cache), mac.digest()) self._last_string = item
def get_content_block(buff): global cmac_keyx hash = hashlib.sha256(buff).digest() key = int16bytes(normalkey(cmac_keyx, keyy)) cipher = CMAC.new(key, ciphermod=AES) result = cipher.update(hash) return result.digest() + b'\x00' * 16
def runTest(self): data_to_mac = get_tag_random("data_to_mac", 128) key = get_tag_random("key", 16) ref_mac = CMAC.new(key, msg=data_to_mac, ciphermod=AES).digest() # Break up in chunks of different length # The result must always be the same for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128: chunks = [data_to_mac[i:i+chunk_length] for i in range(0, len(data_to_mac), chunk_length)] mac = CMAC.new(key, ciphermod=AES) for chunk in chunks: mac.update(chunk) self.assertEqual(ref_mac, mac.digest())
def test_update_after_digest(self): msg = b"rrrrttt" key = b"4" * 16 # Normally, update() cannot be done after digest() h = CMAC.new(key, msg[:4], ciphermod=AES) dig1 = h.digest() self.assertRaises(TypeError, h.update, msg[4:]) dig2 = CMAC.new(key, msg, ciphermod=AES).digest() # With the proper flag, it is allowed h2 = CMAC.new(key, msg[:4], ciphermod=AES, update_after_digest=True) self.assertEquals(h2.digest(), dig1) # ... and the subsequent digest applies to the entire message # up to that point h2.update(msg[4:]) self.assertEquals(h2.digest(), dig2)
def aes_cmac(key, n): if have_crypto: cobj = CMAC.new(key, ciphermod=AES) cobj.update(n) return cobj.digest() else: # return random value return os.urandom(16)
def aes_cmac(key, n): if have_crypto: cobj = CMAC.new(key, ciphermod=AES) cobj.update(n) return cobj.digest() else: # return dummy value return b'\x00' * 16
def create_cmac_object(self, keyslot: int) -> 'CMACObject': """Create a CMAC object with the given keyslot.""" try: key = self.key_normal[keyslot] except KeyError: raise KeyslotMissingError( f'normal key for keyslot 0x{keyslot:02x} is not set up') return CMAC.new(key, ciphermod=AES)
def test_internal_caching(self): """Verify that internal caching is implemented correctly""" data_to_mac = get_tag_random("data_to_mac", 128) key = get_tag_random("key", 16) ref_mac = CMAC.new(key, msg=data_to_mac, ciphermod=AES).digest() # Break up in chunks of different length # The result must always be the same for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128: chunks = [data_to_mac[i:i+chunk_length] for i in range(0, len(data_to_mac), chunk_length)] mac = CMAC.new(key, ciphermod=AES) for chunk in chunks: mac.update(chunk) self.assertEqual(ref_mac, mac.digest())
def test_create_mac(self, tv): self._id = "Wycheproof MAC creation Test #" + str(tv.id) try: tag = CMAC.new(tv.key, tv.msg, ciphermod=AES, mac_len=tv.tag_size).digest() except ValueError as e: if len(tv.key) not in (16, 24, 32) and "key length" in str(e): return raise e if tv.valid: self.assertEqual(tag, tv.tag) self.warn(tv)
def aes128_cmac(key, message): """ (bytes, bytes) -> (bytes) Calculates the AES128 CMAC of a message. :param key: 128 bit long key. (16 bytes sequence). :param message: byte sequence of the message. :return: CMAC of the message (16 bytes sequence). """ assert len(key) == 16, "The key must be 128 bits long." cobj = CMAC.new(key, ciphermod=AES) cobj.update(message) return cobj.digest()
def q2_siv_mode_enc(enc_key, mac_key, plaintext, associated_data): """Question 2 (part 1): Synthetic Initialization Vector (SIV) Authenticated Encryption Your Task: Your function should implement the SIV mode for authenticated encryption as illustrated in lecture 13. For this implementation, you would have to use the AES block cipher in CTR mode, along with CMAC as a MAC. Args: enc_key (str): 16-bytes hex-encoded key to be used for AES mac_key (str): 16-bytes hex-encoded key to be used for CMAC plaintext (str): arbitrary-length ASCII encoded plaintext associated_data (str): arbitrary-length hex-encoded data to be authenticated, but not encrypted Output: ret (str): hex-encoded, ciphertext formatted as tag + ciphertext (as shown in Lecture slides) Test vectors: assert(q2_siv_mode_enc( enc_key="7f7e7d7c7b7a79787776757473727170", mac_key="404142434445464748494a4b4c4d4e4f", plaintext="this is some plaintext to encrypt using SIV-AES", associated_data = "00112233445566778899aabbccddeeffdeaddadadeaddadaffeeddccbbaa99887766554433221100" ) == "2550eb1783787e5f2d4e56fba6dff0a7df554c297854c8c4e4833435e66989314b6b2791862c7d11498c2ef034bfbb63808c73bc5ea23e64cb58a8e1a5775a") Note: Also Feel free to use componenets from the Cryptodome/cryptography libraries to build this function (ex. `from Crypto.Hash import CMAC`). That being said, you should not use the SIV mode provided by any library, you should combine the building blocks to implement the SIV mode on your own. When using the tag as a nonce for the CTR mode, some CTR implementations would not allow the nonce to be equal to the block_size (for example, the `Cryptodome.Cipher` class with throw an error when using a nonce of size > block_size - 1), so I recommend using the CTR mode provided by the library `cryptography` instead (e.g `from cryptography.hazmat.primitives.ciphers import Cipher`). Also note that for this implementation, there's no need to clear any bits of the tag before using it as a nonce. You can assume that the number of blocks we would test against would not overflow the counter bits. """ backend = default_backend() cmac_input = bytes.fromhex(associated_data) + plaintext.encode('ascii') cobj = CMAC.new(bytes.fromhex(mac_key), ciphermod=AES) cobj.update(cmac_input) tag = cobj.digest() cipher = Cipher(algorithms.AES(bytes.fromhex(enc_key)), modes.CTR(tag), backend) encryptor = cipher.encryptor() cipherText = encryptor.update(plaintext.encode('ascii')) return tag.hex() + cipherText.hex()
def ComputMic( self, frame, address, dir, sequence ): key = bytes.fromhex( self.nwkSKey ) b0Block = bytearray( [0x49, 0x00, 0x00, 0x00, 0x00]); b0Block.append( dir ) b0Block.extend( [( address ) & 0xFF, ( address >> 8 ) & 0xFF, ( address >> 16 ) & 0xFF, ( address >> 24 ) & 0xFF] ) b0Block.extend( [( sequence ) & 0xFF, ( sequence >> 8 ) & 0xFF, ( sequence >> 16 ) & 0xFF, ( sequence >> 24 ) & 0xFF] ) b0Block.extend( [0, len( frame ) & 0xFF] ) cmac = CMAC.new( key,ciphermod=AES ) cmac.update( bytes( b0Block + frame ) ) mic = ( cmac.digest()[3] << 24 ) | ( cmac.digest()[2] << 16 ) | ( cmac.digest()[1] << 8 ) | ( cmac.digest()[0] << 0 ) return mic
def q2_siv_mode_enc(enc_key, mac_key, plaintext, associated_data): """Question 2 (part 1): Synthetic Initialization Vector (SIV) Authenticated Encryption""" tag = CMAC.new(binascii.unhexlify(mac_key), (binascii.unhexlify(associated_data) + plaintext.encode()), ciphermod=AES).digest() ciphert = Cipher(algorithms.AES(binascii.unhexlify(enc_key)), mode=modes.CTR(tag), backend=default_backend()) encryptor = ciphert.encryptor() ct = encryptor.update(plaintext.encode()) return binascii.hexlify(tag).decode() + binascii.hexlify(ct).decode()
def create_cmac_object(self, keyslot: Keyslot) -> 'CMAC_CLASS': """ Create a CMAC object with the given keyslot. :param keyslot: :class:`Keyslot` to use. :return: A CMAC object from PyCryptodome. :rtype: CMAC """ try: key = self.key_normal[keyslot] except KeyError: raise KeyslotMissingError(f'normal key for keyslot 0x{keyslot:02x} is not set up') return CMAC.new(key, ciphermod=AES)
def get_challenge(appeui, appkey, appnonce): secret = binascii.a2b_hex(appkey) cobj = CMAC.new(secret, ciphermod=AES) msg = binascii.a2b_hex(appeui) + \ binascii.a2b_hex(str('%08x' % appnonce)) + \ binascii.a2b_hex(str('%08x' % 0)) cobj.update(msg) btr = cobj.digest() challenge = '' for x in range(0, 16): challenge += ('%02X' % btr[16 - x - 1]) # print(challenge) # challenge = cobj.hexdigest() #print(challenge) return challenge
def derive(self): """"Derive a secret from the vector of components. :Return: a byte string, as long as the block length of the cipher. """ if len(self._last_string)>=16: final = self._last_string[:-16] + strxor(self._last_string[-16:], self._cache) else: padded = (self._last_string + bchr(0x80)+ bchr(0)*15)[:16] final = strxor(padded, self._double(self._cache)) mac = CMAC.new(self._key, msg=final, ciphermod=self._ciphermod, cipher_params=self._cipher_params) return mac.digest()
def test_verify_mac(self, tv): self._id = "Wycheproof MAC verification Test #" + str(tv.id) try: mac = CMAC.new(tv.key, tv.msg, ciphermod=AES, mac_len=tv.tag_size) except ValueError as e: if len(tv.key) not in (16, 24, 32) and "key length" in str(e): return raise e try: mac.verify(tv.tag) except ValueError: assert not tv.valid else: assert tv.valid self.warn(tv)
def __init__(self, factory, key, nonce, mac_len, cipher_params): """EAX cipher mode""" self.block_size = factory.block_size """The block size of the underlying cipher, in bytes.""" self.nonce = _copy_bytes(None, None, nonce) """The nonce originally used to create the object.""" self._mac_len = mac_len self._mac_tag = None # Cache for MAC tag # Allowed transitions after initialization self._next = [self.update, self.encrypt, self.decrypt, self.digest, self.verify] # MAC tag length if not (4 <= self._mac_len <= self.block_size): raise ValueError("Parameter 'mac_len' must not be larger than %d" % self.block_size) # Nonce cannot be empty and must be a byte string if len(self.nonce) == 0: raise ValueError("Nonce cannot be empty in EAX mode") if isinstance(nonce, unicode): raise TypeError("nonce must be a byte string") self._omac = [ CMAC.new(key, b'\x00' * (self.block_size - 1) + struct.pack('B', i), ciphermod=factory, cipher_params=cipher_params) for i in xrange(0, 3) ] # Compute MAC of nonce self._omac[0].update(self.nonce) self._signer = self._omac[1] # MAC of the nonce is also the initial counter for CTR encryption counter_int = bytes_to_long(self._omac[0].digest()) self._cipher = factory.new(key, factory.MODE_CTR, initial_value=counter_int, nonce=b"", **cipher_params)
def ComputMic(self, frame, address, dir, sequence): key = bytes.fromhex(self.nwkSKey) b0Block = bytearray([0x49, 0x00, 0x00, 0x00, 0x00]) b0Block.append(dir) b0Block.extend([(address) & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF, (address >> 24) & 0xFF]) b0Block.extend([(sequence) & 0xFF, (sequence >> 8) & 0xFF, (sequence >> 16) & 0xFF, (sequence >> 24) & 0xFF]) b0Block.extend([0, len(frame) & 0xFF]) cmac = CMAC.new(key, ciphermod=AES) cmac.update(bytes(b0Block + frame)) mic = (cmac.digest()[3] << 24) | (cmac.digest()[2] << 16) | ( cmac.digest()[1] << 8) | (cmac.digest()[0] << 0) return mic
def __init__(self, factory, key, nonce, mac_len, cipher_params): """EAX cipher mode""" self.block_size = factory.block_size """The block size of the underlying cipher, in bytes.""" self.nonce = bstr(nonce) """The nonce originally used to create the object.""" self._mac_len = mac_len self._mac_tag = None # Cache for MAC tag # Allowed transitions after initialization self._next = [ self.update, self.encrypt, self.decrypt, self.digest, self.verify ] # MAC tag length if not (4 <= self._mac_len <= self.block_size): raise ValueError("Parameter 'mac_len' must not be larger than %d" % self.block_size) # Nonce cannot be empty and must be a byte string if len(self.nonce) == 0: raise ValueError("Nonce cannot be empty in EAX mode") if isinstance(nonce, unicode): raise TypeError("nonce must be a byte string") self._omac = [ CMAC.new(key, bchr(0) * (self.block_size - 1) + bchr(i), ciphermod=factory, cipher_params=cipher_params) for i in xrange(0, 3) ] # Compute MAC of nonce self._omac[0].update(self.nonce) self._signer = self._omac[1] # MAC of the nonce is also the initial counter for CTR encryption counter_int = bytes_to_long(self._omac[0].digest()) self._cipher = factory.new(key, factory.MODE_CTR, initial_value=counter_int, nonce=b(""), **cipher_params)
def q2_siv_mode_dec(enc_key, mac_key, ciphertext, associated_data): """Question 2 (part 2): Synthetic Initialization Vector (SIV) Authenticated Encryption""" tag = binascii.unhexlify(ciphertext[:32].encode()) ct = binascii.unhexlify(ciphertext[32:].encode()) ciphert = Cipher(algorithms.AES(binascii.unhexlify(enc_key)), mode=modes.CTR(tag), backend=default_backend()) decryptor = ciphert.decryptor() pt = decryptor.update(ct) tagPoss = CMAC.new(binascii.unhexlify(mac_key), (binascii.unhexlify(associated_data) + pt), ciphermod=AES).digest() if tag != tagPoss: return "ERROR" else: return pt.decode()
def aes128_cmac(key: Sequence[int], data: Sequence[int]) -> List[int]: """ AES function as it's used in the LoRaWAN specification. Takes a block of 16 bytes as key, and an arbitrary length of bytes as data and creates a 16 byte CMAC from it. :param key: Key as sequence of bytes :param data: Data as sequence of bytes """ if not isinstance(key, Sequence): raise TypeError("key must be a sequence of 16 bytes") if not isinstance(data, Sequence): raise TypeError("data must be a sequence of bytes") if len(key)!=16 or next((True for k in key if k < 0 or k > 255),False): raise ValueError("key must be a sequence of 16 bytes") if next((True for k in key if k < 0 or k > 255), False): raise ValueError("data must be a sequence of 16 bytes") cmac = CMAC.new(bytes(key), ciphermod=AES) cmac.update(bytes(data)) return [c for c in cmac.digest()]
def aes_cmac(key, n): cobj = CMAC.new(key, ciphermod=AES) cobj.update(n) return cobj.digest()
def _real_extract(self, url): video_id = self._match_id(url) data = json.dumps({ 'method': 'da.content.get', 'params': [ video_id, { 'site': 's%d', 'referrer': 'http://www.ivi.ru/watch/%s' % video_id, 'contentid': video_id } ] }) bundled = hasattr(sys, 'frozen') for site in (353, 183): content_data = (data % site).encode() if site == 353: if bundled: continue try: from Cryptodome.Cipher import Blowfish from Cryptodome.Hash import CMAC pycryptodomex_found = True except ImportError: pycryptodomex_found = False continue timestamp = (self._download_json( self._LIGHT_URL, video_id, 'Downloading timestamp JSON', data=json.dumps({ 'method': 'da.timestamp.get', 'params': [] }).encode(), fatal=False) or {}).get('result') if not timestamp: continue query = { 'ts': timestamp, 'sign': CMAC.new(self._LIGHT_KEY, timestamp.encode() + content_data, Blowfish).hexdigest(), } else: query = {} video_json = self._download_json(self._LIGHT_URL, video_id, 'Downloading video JSON', data=content_data, query=query) error = video_json.get('error') if error: origin = error.get('origin') message = error.get('message') or error.get('user_message') extractor_msg = 'Unable to download video %s' if origin == 'NotAllowedForLocation': self.raise_geo_restricted(message, self._GEO_COUNTRIES) elif origin == 'NoRedisValidData': extractor_msg = 'Video %s does not exist' elif site == 353: continue elif bundled: raise ExtractorError( 'This feature does not work from bundled exe. Run youtube-dl from sources.', expected=True) elif not pycryptodomex_found: raise ExtractorError( 'pycryptodomex not found. Please install it.', expected=True) elif message: extractor_msg += ': ' + message raise ExtractorError(extractor_msg % video_id, expected=True) else: break result = video_json['result'] title = result['title'] quality = qualities(self._KNOWN_FORMATS) formats = [] for f in result.get('files', []): f_url = f.get('url') content_format = f.get('content_format') if not f_url or '-MDRM-' in content_format or '-FPS-' in content_format: continue formats.append({ 'url': f_url, 'format_id': content_format, 'quality': quality(content_format), 'filesize': int_or_none(f.get('size_in_bytes')), }) self._sort_formats(formats) compilation = result.get('compilation') episode = title if compilation else None title = '%s - %s' % (compilation, title) if compilation is not None else title thumbnails = [{ 'url': preview['url'], 'id': preview.get('content_format'), } for preview in result.get('preview', []) if preview.get('url')] webpage = self._download_webpage(url, video_id) season = self._search_regex( r'<li[^>]+class="season active"[^>]*><a[^>]+>([^<]+)', webpage, 'season', default=None) season_number = int_or_none( self._search_regex( r'<li[^>]+class="season active"[^>]*><a[^>]+data-season(?:-index)?="(\d+)"', webpage, 'season number', default=None)) episode_number = int_or_none( self._search_regex( r'[^>]+itemprop="episode"[^>]*>\s*<meta[^>]+itemprop="episodeNumber"[^>]+content="(\d+)', webpage, 'episode number', default=None)) description = self._og_search_description( webpage, default=None) or self._html_search_meta( 'description', webpage, 'description', default=None) return { 'id': video_id, 'title': title, 'series': compilation, 'season': season, 'season_number': season_number, 'episode': episode, 'episode_number': episode_number, 'thumbnails': thumbnails, 'description': description, 'duration': int_or_none(result.get('duration')), 'formats': formats, }
def _aes_cmac(key: bytes, msg: bytes) -> bytes: return CMAC.new(key, msg, ciphermod=AES).digest()
def getCMAC(msed): hash = hashlib.sha256(msed[:0x110]).digest() key = int16bytes(nkey_0x35mac) cipher = CMAC.new(key, ciphermod=AES) result = cipher.update(hash) return result.digest()
def recv_msg3_verify(self, msg3: bytes) -> Tuple[dict, str]: pre_quote_len = 16 + 32 + 32 + 256 io = BytesIO(msg3) mac = io.read(16) g_a = io.read(32 + 32) ps_sec_prop = io.read(256) version = io.read(2) sign_type = io.read(2) epid_group_id = io.read(4) qe_svn = io.read(2) pce_svn = io.read(2) xeid = io.read(4) basename = io.read(32) cpu_svn = io.read(16) # Security Version of the CPU misc_select = io.read(4) # Which fields defined in SSA.MISC _ = io.read(12) # isv_ext_prod_id = io.read(16) # ISV assigned Extended Product ID attributes = io.read( 16) # Any special Capabilities the Enclave possess mr_enclave = io.read( 32) # The value of the enclave's ENCLAVE measurement _ = io.read(32) # mr_signer = io.read( 32) # The value of the enclave's SIGNER measurement _ = io.read(32) # config_id = io.read(64) # CONFIGID isv_prod_id = io.read(2) # Product ID of the Enclave isv_svn = io.read(2) # Security Version of the Enclave config_svn = io.read(2) # CONFIGSVN reserved4 = io.read(42) # isv_family_id = io.read(16) # ISV assigned Family ID report_data = io.read(64) # Data provided by the user signature_len = _load_int(io.read(4)) signature = io.read(signature_len) # Verify that Ga in msg3 matches Ga in msg1. Ga_ser = _serialize_ec_point(self.Ga.pointQ) Gb_ser = _serialize_ec_point(self.Gb.pointQ) if Ga_ser != g_a: return None, "Step1: Peer PK doesn't match to the previous one" # Verify CMAC_SMK(M). m = msg3[16:pre_quote_len + 436 + signature_len] try: CMAC.new(self.smk, msg=m, ciphermod=AES).verify(mac) except ValueError: return None, "Step2: Invalid MAC" # Verify that the first 32-bytes of the report data match the SHA-256 # digest of (Ga || Gb || VK), where || denotes concatenation. # VK is derived by performing an AES-128 CMAC over the following byte # sequence, using the KDK as the key: # 0x01 || "VK" || 0x00 || 0x80 || 0x00 vk = _aes_cmac(key=self.kdk, msg=self.SEED_VK) keyhash = _sha256(Ga_ser, Gb_ser, vk).digest() if report_data[:32] != keyhash: if VERBOSE: print('>>> First 32-bytes of the report data') hexdump(report_data[:32]) print('>>> SHA256(Ga||Gb||VK)') hexdump(keyhash) return None, "Step3: Key Hash doesn't match" if self.sim: attestation_report = {} advisory_url = '' else: # Verify the attestation evidence provided by the client. # 1. Extract the quote from msg3. quote = msg3[pre_quote_len:pre_quote_len + 436 + signature_len] if VERBOSE: print('>>> quote') hexdump(quote) # 2. Submit the quote to IAS, calling the API function to verify attestation evidence. request_id, report_signature, cert_chain, advisory_url, _, response = self.ias.verify_attestation_evidence( quote) # TODO: 3. Validate the signing certificate received in the report response. # TODO: 4. Validate the report signature using the signing certificate. # If the quote is successfully validated in Step 3, perform the following: # 1. Extract the attestation status for the enclave and, if provided, the PSE. attestation_report = json.loads(response) # TODO: 2. Examine the enclave identity (MRSIGNER), security version and product ID. # TODO: 3. Examine the debug attribute and ensure it is not set (in a production environment). # TODO: 4. Decide whether or not to trust the enclave and, if provided, the PSE. # Derive the session keys, SK and MK, that should be used to transmit # future messages between the client and server during the session. # The client can simply call sgx_ra_get_keys(), but the server must # derive them manually by performing an AES-128 CMAC over the following # byte sequences, using the KDK as the key: # MK: 0x01 || "MK" || 0x00 || 0x80 || 0x00 # SK: 0x01 || "SK" || 0x00 || 0x80 || 0x00 self.mk = _aes_cmac(key=self.kdk, msg=self.SEED_MK) self.sk = _aes_cmac(key=self.kdk, msg=self.SEED_SK) # Generate msg4 and send it to the client. return attestation_report, advisory_url