67 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			67 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
| #!/usr/bin/env python3
 | |
| 
 | |
| from pathlib import Path
 | |
| import struct
 | |
| import hashlib
 | |
| import argparse
 | |
| v1_prod_pubkey = 0xC3E748CAD9CD384329E10E25A91E43E1A762FF529ADE578C935BDDF9B13F2179D4855E6FC89E9E29CA12517D17DFA1EDCE0BEBF0EA7B461FFE61D94E2BDF72C196F89ACD3536B644064014DAE25A15DB6BB0852ECBD120916318D1CCDEA3C84C92ED743FC176D0BACA920D3FCF3158AFF731F88CE0623182A8ED67E650515F75745909F07D415F55FC15A35654D118C55A462D37A3ACDA08612F3F3F6571761EFCCBCC299AEE99B3A4FD6212CCFFF5EF37A2C334E871191F7E1C31960E010A54E86FA3F62E6D6905E1CD57732410A3EB0C6B4DEFDABE9F59BF1618758C751CD56CEF851D1C0EAA1C558E37AC108DA9089863D20E2E7E4BF475EC66FE6B3EFDCF
 | |
| # v2_prod_pubkey = 0xCB45C5E53217D4499FB80B2D96AA4F964EB551F1DA4EBFA4F5E23F87BFE82FC113590E536757F329D6EAD1F267771EE342F5A5E61514DD3D3383187E663929D577D94648F262EBA1157E152DB5273D10AE3A6A058CB9CD64D01267DAC82ED3B7BC1631D078C911414129CDAAA0FFB0A8E2A7ADD6F32FB09A7E98D259BFF6ED10808D1BDA58CAF7355DFF1A085A18B11657D2617447BF657140D599364E5AC8E626276AC03BC2417831D9E61B25154AFE9F2D8271E9CE22D2783803083A5A7A575774688721097DC5E4B32D118CF6317A7083BA15BA608430A8C8C6B7DA2D932D81F571603A9363AC0197AB670242D9C9180D97A10900F11FE3D9246CF14F0883
 | |
| # v2_dev_pubkey  = 0xB372CEC9E05E71FB3FAA08C34E3256FB312EA821638A243EF8A5DEA46FCDA33F00F88FC2933FB276D37B914F89BAD5B5D75771E342265B771995AE8F43B4DFF3F21A877FE777A8B419587C8718D36204FA1922A575AD5207D5D6B8C10F84DDCA661B731E7E7601D64D4A894F487FE1AA1DDC2A1697A3553B1DD85D5750DF2AA9D988E83C4C70BBBE4747219F9B92B199FECB16091896EBB441606DEC20F446249D5568BB51FC87BA7F85E6295FBE811B0A314408CD31921C360608A0FF7F87BD733560FE1C96E472834CAB6BE016C35727754273125089BE043FD3B26F0B2DE141E05990CE922F1702DA0A2F4E9F8760D0FA712DDB9928E0CDAC14501ED5E2C3
 | |
| 
 | |
| ChunkListHeader = struct.Struct('<4sIBBBxQQQ')
 | |
| assert ChunkListHeader.size == 0x24
 | |
| 
 | |
| Chunk = struct.Struct('<I32s')
 | |
| assert Chunk.size == 0x24
 | |
| 
 | |
| def parse_chunklist(path):
 | |
|     with open(path, 'rb') as f:
 | |
|         hash_ctx = hashlib.sha256()
 | |
|         data = f.read(ChunkListHeader.size)
 | |
|         hash_ctx.update(data)
 | |
|         magic, header_size, file_version, chunk_method, signature_method, chunk_count, chunk_offset, signature_offset = ChunkListHeader.unpack(data)
 | |
|         assert magic == b'CNKL'
 | |
|         assert header_size == ChunkListHeader.size
 | |
|         assert file_version == 1
 | |
|         assert chunk_method == 1
 | |
|         assert signature_method in [1, 2]
 | |
|         assert chunk_count > 0
 | |
|         assert chunk_offset == 0x24
 | |
|         assert signature_offset == chunk_offset + Chunk.size * chunk_count
 | |
|         for i in range(chunk_count):
 | |
|             data = f.read(Chunk.size)
 | |
|             hash_ctx.update(data)
 | |
|             chunk_size, chunk_sha256 = Chunk.unpack(data)
 | |
|             yield chunk_size, chunk_sha256
 | |
|         digest = hash_ctx.digest()
 | |
|         if signature_method == 1:
 | |
|             data = f.read(256)
 | |
|             assert len(data) == 256
 | |
|             signature = int.from_bytes(data, 'little')
 | |
|             plaintext = 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004200000000000000000000000000000000000000000000000000000000000000000 | int.from_bytes(digest, 'big')
 | |
|             assert pow(signature, 0x10001, v1_prod_pubkey) == plaintext
 | |
|         elif signature_method == 2:
 | |
|             data = f.read(32)
 | |
|             assert data == digest
 | |
|         else:
 | |
|             raise NotImplementedError
 | |
|         assert f.read(1) == b''
 | |
| 
 | |
| def check_chunklist(path, chunklist_path):
 | |
|     with open(path, 'rb') as f:
 | |
|         for chunk_size, chunk_sha256 in parse_chunklist(chunklist_path):
 | |
|             chunk = f.read(chunk_size)
 | |
|             assert len(chunk) == chunk_size
 | |
|             assert hashlib.sha256(chunk).digest() == chunk_sha256
 | |
|         assert f.read(1) == b''
 | |
| 
 | |
| def main():
 | |
|     parser = argparse.ArgumentParser()
 | |
|     parser.add_argument('vmdir', type=Path)
 | |
|     args = parser.parse_args()
 | |
|     vmdir = args.vmdir
 | |
|     check_chunklist(vmdir / 'RecoveryImage.dmg', vmdir / 'RecoveryImage.chunklist')
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     main()
 |