def decrypt(prx, meta, **kwargs): p = prx_header_8(prx) xorbuf = kirk.kirk7(meta['seed'], meta['key']) # calculate SHA1 of header h = SHA1.new() h.update(xorbuf[:0x14]) h.update(p.vanity_area()) h.update(p.kirk_block()) h.update(p.kirk_metadata()) h.update(p.elf_info()) if h.digest() != p.sha1_hash(): print("bad SHA1") return False # decrypt the kirk header header = xor(p.kirk_block(), xorbuf[0x14:0x84]) header = kirk.kirk7(header, meta['key']) header = xor(header, xorbuf[0x20:]) # prepare the kirk block block = header + p.kirk_metadata() + p.elf_info() + prx[0x150:] # do the decryption return kirk.kirk1(block)
def decrypt(prx, meta, **kwargs): xorbuf = expand_seed(meta['seed'], meta['key']) # check if range contains nonzero if any(x != 0 for x in prx[0xD4:0xD4 + 0x30]): return False p = prx_header_9(prx) print(meta['pubkey']) print(p.prx_ecdsa().hex()) # check ECDSA signature # kirk.kirk11(bytes.fromhex(meta['pubkey']), p.prx_ecdsa( # ), prx[4:0x104] + b'\x00'*0x28 + prx[0x12C:]) h2 = SHA1.new() h2.update(prx[4:0x104] + b'\x00' * 0x28 + prx[0x12C:]) print(h2.hexdigest()) # decrypt the header information p.decrypt_header(meta['key']) # calculate SHA1 of header h = SHA1.new() h.update(p.tag()) h.update(xorbuf[:0x10]) h.update(b'\x00' * 0x58) h.update(p.btcnf_id()) h.update(p.kirk_aes_key()) h.update(p.kirk_cmac_key()) h.update(p.kirk_cmac_header_hash()) h.update(p.kirk_cmac_data_hash()) h.update(p.kirk_metadata()) h.update(p.elf_info()) # sanity check that our SHA1 actually matches if h.digest() != p.sha1_hash(): return False # decrypt the kirk block header = xor(p.kirk_block(), xorbuf[0x10:0x50]) header = kirk.kirk7(header, meta['key']) header = xor(header, xorbuf[0x50:]) # prepare the kirk block block = header + b'\x00' * 0x30 block = set_kirk_cmd_1(block) block = block + p.kirk_metadata() + b'\x00'*0x10 + \ p.elf_info() + prx[0x150:] return kirk.kirk1(block)
def _demangle(self, block): if self._version == 5: block = xor( block, math.ceil(len(block) / 0x10) * bytes.fromhex('D869B895336B633498B9FC3CB7262BD7')[:len(block)]) block = kirk.kirk7(block, 0x55) if self._version == 5: block = xor( block, math.ceil(len(block) / 0x10) * bytes.fromhex('0DA09084AF9EB6E2D294F2AAEF996871')[:len(block)]) return block
def encrypt(prx, meta, id=None): xorbuf = expand_seed(meta['seed'], meta['key']) # encrypt as kirk1 encrypted = kirk.kirk1_encrypt_ecdsa(prx[0x150:], salt=prx[:0x80]) header = xor(encrypted[:0x40], xorbuf[0x50:]) header = kirk.kirk4(header, meta['key']) header = xor(header, xorbuf[0x10:0x50]) # calculate an id if id == None: id = Random.get_random_bytes(16) elif type(id) is str: id = '{:16.16}'.format(id).encode() id = kirk.kirk7(id, meta['key']) # create a prx header prx_header = prx_header_6() prx_header.set_elf_info(prx[:0x80]) prx_header.set_kirk_block(header) prx_header.set_kirk_ecdsa_data_sig_end(encrypted[0x40:0x60]) prx_header.set_kirk_metadata(encrypted[0x70:0x80]) prx_header.set_btcnf_id(id) prx_header.set_tag(prx[0xD0:0xD4]) # calculate SHA1 of header h = SHA1.new() h.update(prx_header.tag()) h.update(xorbuf[:0x10]) h.update(b'\x00' * 0x38) h.update(prx_header.kirk_ecdsa_data_sig_end()) h.update(prx_header.btcnf_id()) h.update(prx_header.kirk_aes_key()) h.update(prx_header.kirk_ecdsa_header_sig()) h.update(prx_header.kirk_ecdsa_data_sig_begin()) h.update(prx_header.kirk_metadata()) h.update(prx_header.elf_info()) prx_header.set_sha1_hash(h.digest()) # encrypt the header and return the complete PRX prx_header.encrypt_header(meta['key']) return prx_header.prx() + encrypted[0x90 + 0x80:]
def decrypt(prx, meta): xorbuf = expand_seed(meta['seed'], meta['key']) # check if range contains nonzero if any(x != 0 for x in prx[0xD4:0xD4 + 0x38]): return False p = prx_header_6(prx) # decrypt the header information p.decrypt_header(meta['key']) # calculate SHA1 of header h = SHA1.new() h.update(p.tag()) h.update(xorbuf[:0x10]) h.update(b'\x00' * 0x38) h.update(p.kirk_ecdsa_data_sig_end()) h.update(p.btcnf_id()) h.update(p.kirk_aes_key()) h.update(p.kirk_ecdsa_header_sig()) h.update(p.kirk_ecdsa_data_sig_begin()) h.update(p.kirk_metadata()) h.update(p.elf_info()) if h.digest() != p.sha1_hash(): print("bad SHA1") return False # decrypt the kirk header header = xor(p.kirk_block(), xorbuf[0x10:0x50]) header = kirk.kirk7(header, meta['key']) header = xor(header, xorbuf[0x50:]) # prepare the kirk block block = header + p.kirk_ecdsa_data_sig_end() + b'\x00' * 0x10 block = set_kirk_cmd_1(block) block = set_kirk_cmd_1_ecdsa(block) block = block + p.kirk_metadata() + b'\x00'*0x10 + \ p.elf_info() + prx[0x150:] # do the decryption return kirk.kirk1(block)
def encrypt(prx, meta, vanity=None, **kwargs): xorbuf = kirk.kirk7(meta['seed'], meta['key']) # encrypt as kirk1 encrypted = kirk.kirk1_encrypt_ecdsa(prx[0x150:], salt=prx[:0x80]) header = xor(encrypted[:0x70], xorbuf[0x20:]) header = kirk.kirk4(header, meta['key']) header = xor(header, xorbuf[0x14:0x84]) # calculate some vanity if vanity == None: vanity = Random.get_random_bytes(0x28) elif type(vanity) is str: vanity = '{:40.40}'.format(vanity).encode() # create a prx header prx_header = prx_header_8() prx_header.set_elf_info(prx[:0x80]) prx_header.set_kirk_block(header) prx_header.set_kirk_metadata(encrypted[0x70:0x90]) prx_header.set_vanity_area(vanity) prx_header.set_tag(prx[0xD0:0xD4]) # calculate SHA1 of header h = SHA1.new() h.update(xorbuf[:0x14]) h.update(prx_header.vanity_area()) h.update(prx_header.kirk_block()) h.update(prx_header.kirk_metadata()) h.update(prx_header.elf_info()) prx_header.set_sha1_hash(h.digest()) # encrypt the header and return the complete PRX return prx_header.prx() + encrypted[0x90 + 0x80:]
def verifyAnchorCanData(current_anchor_random_number, can_id_key, nonce, ciphertext): """Decrypt and verify a CAN data frame Parameters - **current_anchor_random_number** *byte string* : last anchor random number received by the ECU - **can_id_key** *byte string* : private key corresponding to the CAN ID used for this communication - **nonce** *byte string* : initial vector or latest CAN frame for the CAN ID used for this communication - **ciphertext** *byte string* : encrypted CAN data frame """ # Key derivation function salt = current_anchor_random_number kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=64, salt=salt, iterations=10000, backend=default_backend()) kdf_output = kdf.derive(can_id_key) ctr_counter = Counter.new( 120, nonce ) #counter size will be 120 bits + *nonce size* bits. Counter size should be as big as block size (https://pythonhosted.org/pycrypto/Crypto.Cipher.blockalgo-module.html#MODE_CTR) block_cipher = AES.new(can_id_key, AES.MODE_CTR, counter=ctr_counter) derived_key = block_cipher.encrypt(kdf_output)[:8] # Decryption data_frame = xor(derived_key, ciphertext) message = data_frame[:6] counter = data_frame[6:7] received_hash = data_frame[7:8] # Authentication check hmac = HMAC.new(kdf_output, digestmod=SHA256) hmac.update(message + counter + current_anchor_random_number) authentication = hmac.hexdigest().encode()[:1] #TODO: add Ta (counter) check if authentication == received_hash: a = 1 #print("\nSuccesful authentication, message is: " + str("".join("\\x%02x" % i for i in message))) else: a = 2
def verifyAnchorFrame(gateway_private_key, gateway_initial_vector, ciphertext): """Decrypt and verify an anchor frame Parameters - **gateway_private_key** *byte string* : private key of the gateway ECU - **initial vector** *byte string* : initial vector of the gateway ECU - **ciphertext** *byte string* : encrypted anchor frame """ # Key derivation function salt = gateway_initial_vector kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=64, salt=salt, iterations=10000, backend=default_backend()) kdf_output = kdf.derive(gateway_private_key) ctr_counter = Counter.new( 120, gateway_initial_vector ) #counter size will be 120 bits + *gateway_initial_vector size* bits. Counter size should be as big as block size (https://pythonhosted.org/pycrypto/Crypto.Cipher.blockalgo-module.html#MODE_CTR) block_cipher = AES.new(gateway_private_key, AES.MODE_CTR, counter=ctr_counter) derived_key = block_cipher.encrypt(kdf_output) # Decryption data_frame = xor(derived_key, ciphertext) anchor_random_number = data_frame[:56] received_hash = data_frame[56:64] # Authentication check hmac = HMAC.new(kdf_output, digestmod=SHA256) hmac.update(anchor_random_number) authentication = hmac.hexdigest().encode()[:8] if authentication == received_hash: print("\nSuccesful authentication, anchor random number is: " + str("".join("\\x%02x" % i for i in anchor_random_number))) else: print("\nFailed authentication, can't retrieve anchor random number!")
def generateAnchorCanData(current_anchor_random_number, message, can_id_key, can_id_counter, nonce): """Return an encrypted CAN data frame Parameters - **current_anchor_random_number** *byte string* : last anchor random number received by the ECU - **message** *byte string* : data to be sent securely - **can_id_key** *byte string* : private key corresponding to the CAN ID used for this communication - **can_id_counter** *byte string* : local counter corresponding to the CAN ID used for this communication - **nonce** *byte string* : initial vector or latest CAN frame for the CAN ID used for this communication """ # TODO: understand why can_id_counter is only 2 bits while documentation recommends 4bits # Key derivation function salt = current_anchor_random_number kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=64, salt=salt, iterations=10000, backend=default_backend()) kdf_output = kdf.derive(can_id_key) ctr_counter = Counter.new( 120, nonce ) #counter size will be 120 bits + *nonce size* bits. Counter size should be as big as block size (https://pythonhosted.org/pycrypto/Crypto.Cipher.blockalgo-module.html#MODE_CTR) block_cipher = AES.new(can_id_key, AES.MODE_CTR, counter=ctr_counter) derived_key = block_cipher.encrypt(kdf_output)[:8] # Authentication hmac = HMAC.new(kdf_output, digestmod=SHA256) hmac.update(message + can_id_counter + current_anchor_random_number) authentication = hmac.hexdigest().encode()[:1] # Encryption data_frame = message + can_id_counter + authentication ciphertext = xor(data_frame, derived_key) return ciphertext
def generateAnchorFrame(anchor_random_number, gateway_private_key, gateway_initial_vector): """Return an anchor frame (an encrypted random number ready to be securely sent to ECUs) Parameters - **anchor_random_number** *byte string* : random number to send to the ECUs - **gateway_private_key** *byte string* : private key of the gateway ECU - **initial vector** *byte string* : initial vector of the gateway ECU """ # Key derivation function salt = gateway_initial_vector kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=64, salt=salt, iterations=10000, backend=default_backend()) kdf_output = kdf.derive(gateway_private_key) ctr_counter = Counter.new( 120, gateway_initial_vector ) #counter size will be 120 bits + *gateway_initial_vector size* bits. Counter size should be as big as block size (https://pythonhosted.org/pycrypto/Crypto.Cipher.blockalgo-module.html#MODE_CTR) block_cipher = AES.new(gateway_private_key, AES.MODE_CTR, counter=ctr_counter) derived_key = block_cipher.encrypt(kdf_output) # Authentication hmac = HMAC.new(kdf_output, digestmod=SHA256) hmac.update(anchor_random_number) authentication = hmac.hexdigest().encode()[:8] # Encryption data_frame = anchor_random_number + authentication ciphertext = xor(data_frame, derived_key) return ciphertext