Question Overview
Question link: Level II Challenges | MysteryTwister – The Cipher Contest
Full translation, Ref: https://www.codenong.com/cs109540921/
1. The AES encryption mode is CBC, the initialization vector (IV) is zero, and the padding is 01-00. Furthermore, the corresponding key is in a form such as the Machine Readable Zone (MRZ) on the ID document, which is not entirely complete when used with European e-passports.
2. The goal is to find the clear text of the following base64 encoded message:
9MgYwmuPrjiecPMx61O6zIuy3MtIXQQ0E59T3xB6u0Gyf1gYs2i3K9Jxaa0zj4gTMazJuApwd6 + jdyeI5iGHvhQyDHGVlAuYTgJrbFDrfB22Fpil2NfNnWFBTXyf7SDI
3. For encryption, the key KENC based on the Basic Access Control (BAC) protocol has been generated and applied. For decryption, the following characters have been sent, from which KENC can be derived (the encoding type of these characters is described in [1]):
12345678 <8 <<< 1110182 <111116? <<<<<<<<<<<<<< 4
(That’s the one in the picture)
4. Lost during transfer and highlighted with a ”? ”. It can be restored with the help of [2]. To be able to calculate the key KENC later, an overview of the applied encoding can be found [3], a protocol in [4] and an example in [5].
5. Decode the base64 code before decrypting.
All in all, it is to restore the encrypted plain text based on the string of numbers it gives, and then follow the method used in the reference article.
Problem-solving ideas
1. Find unknown numbers
2. Calculate the key based on the obtained number.
3. Decrypt the ciphertext decoded by key and base64
Code analysis
1. Find unknown numbers
Design ideas:
- In order to calculate the check digit of the expiration date, I designed a function
Unknown_Number
. - In this function, the preset number
number
and weightweight
are used. - Iterate through each number in
number
and multiply it by the corresponding weight. - Finally, the sum of these products is returned modulo 10, which is the unknown number required.
def Unknown_Number() -> int: Unknown_Number = 0 number = "111116" # Default number weight = "731" # Weight for i in range(0, len(number)): Unknown_Number + = int(number[i]) * int(weight[i % 3]) return Unknown_Number % 10 # Return check digit
2. Calculate the key based on the obtained number
Design ideas:
- First use a function
cal_Kseed
to calculateK_seed
. - This function takes the passport information
MRZ_information
and performs a SHA1 hash on it. - Take the first 32 bits of the hash value as
K_seed
.
def cal_Kseed() -> str: MRZ_information = "12345678<811101821111167" # Passport information H_information = sha1(MRZ_information.encode()).hexdigest() # Use SHA1 for hashing K_seed = H_information[0:32] # Take the first 32 bits of the hash value as K_seed return K_seed
- Next, use the function
cal_Ka_Kb
to calculateka
andkb
. - Concatenates
K_seed
with a constantc
and performs a SHA1 hash on it. - Take the first 16 digits of the hash value as
ka
, and the last 16 digits askb
.
def cal_Ka_Kb(K_seed): c = "00000001" d = K_seed + c H_d = sha1(codecs.decode(d, "hex")).hexdigest() # Hash K_seed ka = H_d[0:16] # Take the first 16 digits as ka kb = H_d[16:32] # Take the last 16 bits as kb return ka, kb
- Finally, use the function
Parity_Check
to perform parity check onka
andkb
, and get the newk_1
andk_2
. - Merge
k_1
andk_2
as the final key.
def Parity_Check(x): k_list = [] a = bin(int(x, 16))[2:] # Convert hexadecimal to binary for i in range(0, len(a), 8): # Divide a group of 7 bits into blocks and calculate a check digit to make the number of 1's an even number. if (a[i:i + 7].count("1")) % 2 == 0: k_list.append(a[i:i + 7]) k_list.append('1') else: k_list.append(a[i:i + 7]) k_list.append('0') k = hex(int(''.join(k_list), 2)) # Convert binary to hexadecimal return k
3. Decrypt the ciphertext decoded by key and base64
Design ideas:
- First, a given ciphertext is base64 decoded.
- Set an initialization vector
IV
. - Use the CBC mode of AES and the previously calculated key to decrypt the ciphertext.
- Finally, the decrypted plaintext is output.
ciphertext = base64.b64decode( "9MgYwmuPrjiecPMx61O6zIuy3MtIXQQ0E59T3xB6u0Gyf1gYs2i3K9Jxaa0zj4gTMazJuApwd6 + jdyeI5iGHvhQyDHGVlAuYTgJrbFDrfB22Fpil2NfNnWFBTXyf7SDI") IV = '0' * 32 # Initialization vector # Decrypt using AES m = AES.new(binascii.unhexlify(key), AES.MODE_CBC, binascii.unhexlify(IV)).decrypt(ciphertext) print(m) # Output the decrypted plain text
Answer
Full code:
from hashlib import sha1 import codecs import base64 from Crypto.Cipher import AES import binascii # Find the unknown number, which is the check digit of the expiration date, calculated according to the check rules def Unknown_Number() -> int: Unknown_Number = 0 number = "111116" # Default number weight = "731" # Weight for i in range(0, len(number)): Unknown_Number + = int(number[i]) * int(weight[i % 3]) return Unknown_Number % 10 # Return check digit # Calculate k_seed def cal_Kseed() -> str: MRZ_information = "12345678<811101821111167" # Passport information H_information = sha1(MRZ_information.encode()).hexdigest() # Use SHA1 for hashing K_seed = H_information[0:32] # Take the first 32 bits of the hash value as K_seed return K_seed def cal_Ka_Kb(K_seed): c = "00000001" d = K_seed + c H_d = sha1(codecs.decode(d, "hex")).hexdigest() # Hash K_seed ka = H_d[0:16] # Take the first 16 digits as ka kb = H_d[16:32] # Take the last 16 bits as kb return ka, kb # Perform parity checks on Ka and Kb respectively to obtain new k1 and k2 def Parity_Check(x): k_list = [] a = bin(int(x, 16))[2:] # Convert hexadecimal to binary for i in range(0, len(a), 8): # Divide a group of 7 bits into blocks and calculate a check digit to make the number of 1's an even number. if (a[i:i + 7].count("1")) % 2 == 0: k_list.append(a[i:i + 7]) k_list.append('1') else: k_list.append(a[i:i + 7]) k_list.append('0') k = hex(int(''.join(k_list), 2)) # Convert binary to hexadecimal return k if __name__ == "__main__": K_seed = cal_Kseed() # Calculate K_seed ka, kb = cal_Ka_Kb(K_seed) # Calculate ka and kb k_1 = Parity_Check(ka) # Perform parity check on ka k_2 = Parity_Check(kb) # Perform parity check on kb key = k_1[2:] + k_2[2:] # Combine k_1 and k_2 as the final key print(key) # Output key # Cipher text to be decrypted ciphertext = base64.b64decode( "9MgYwmuPrjiecPMx61O6zIuy3MtIXQQ0E59T3xB6u0Gyf1gYs2i3K9Jxaa0zj4gTMazJuApwd6 + jdyeI5iGHvhQyDHGVlAuYTgJrbFDrfB22Fpil2NfNnWFBTXyf7SDI") IV = '0' * 32 # Initialization vector # Decrypt using AES m = AES.new(binascii.unhexlify(key), AES.MODE_CBC, binascii.unhexlify(IV)).decrypt(ciphertext) print(m) # Output the decrypted plain text
The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. Python entry skill treeHomepageOverview 376,186 people are learning the system