- Two identification numbers unique to each device are generated from the NOR flash and baseband CPU serials: the norID and the chipID, 8 respectively 12 bytes in size.
- The device-specific deviceKey is generated from truncating a SHA1 hash of the concatenated and padded norID and chipID.
- A supposedly random NCK ('network control key') is SHA1-hashed. With the hashed NCK and the norID and chipID, the second key nckKey is generated. The hashing algorithm uses Tiny Encryption Algorithm (TEA). The nckKey is also device-specific since both the norID and chipID are used.
- A device-specific RSA signature is generated: two SHA1 hashes are generated from the norID and chipID. The status that the lock has after the correct NCK has been entered is also embedded into this message. The PCKS 1.5 format is used to pad the hashes and the status from (2*160+32) bit to 2048 bit (256 byte).
- The asymmetric RSA algorithm is used for the encryption of the unlock signature. Keep in mind that the algorithm uses two different keys: a private key for encryption and a public key for decryption. With the private RSA key, the signature is encrypted and stored in protected memory.
- This signature is encrypted with TEA once again using the device-specific deviceKey in CBC mode.
deviceKey = SHA1_hash(norID+chipID)
nckKey = custom_hash(norID, chipID, SHA1_hash(NCK), deviceKey)
rawSignature = generateSignature(SHA1_hash(norID+chipID), SHA1_hash(chipID))
Signature = RSA_encrypt(rawSignature, privateRSAkey)
encryptedSignature = TEA_encrypt_cbc(Signature, nckKey)The encryptedSignature is then saved to a protected memory area - the device has been locked. This happens when Apple issues the AT+CLCK="PN",1,"NCK" command presumably directly after manufacturing the phone.
When testing a network code key, the baseband firmware reads the encryptedSignature, calculates the deviceKey and the nckKey from the entered NCK, decrypts the encryptedSignature with the nckKey using TEA, decrypts it once more with the public RSA key and verifies the signature with the SHA1 hashes of the chipID / norID. Here's the pseudo code:
deviceKey = SHA1_hash(norID+chipID)
nckKey = custom_hash(norID, chipID, SHA1_hash(NCK), deviceKey)
encryptedSignature = readEncryptedSignature()
Signature = TEA_decrypt_cbc(encryptedSignature, nckKey)
rawSignature = RSA_decrypt(Signature, publicRSAKey)
if ( (rawSignature has correct format) and (rawSignature contains both SHA1_hash(norID+chipID), SHA1_hash(chipID)) and (Lock status byte in rawSignature is OK) )
.. accept every SIM card
else
.. block non-authorized SIMs
A correct NCK key can be stored the application processor part of device. When a certain flag is set, the application firmware (iOS) feeds the NCK into the baseband modem during the boot-up. If the decrypted rawSignature passes the check, the baseband unlocks.
On top of this, a WildcardTicket mechanism has been implemented on 3G and later devices. However, it is quite noteworthy that the WildcardTicket mechanism is overriden if the NCK can be verified (3G/3GS).
Various lessons can be learned from this:
- The NCK is only stored indirectly on the device in a protected area.
- The signature which contains the information about the NCK is directly linked to the device. Hence, replicating a signature from another device will not work.
- The NCK is a 15 digit number which is presumably not dependent on the IMEI or any other serial number, but completely random.
- Brute force attacks are foiled because a few expensive operations are necessary just to verify the code and the key space is large, e.g. the number of possible key combinations is big.
- A valid signature is implicitly required for an unlocked device. Factory-unlocked devices are shipped with such a signature, and during the official unlock process, this signature is generated.
- A fake signature for a device with known norID, chipID and NCK can not be generated because the private RSA key is unknown.
- Consequent code signing makes permanent firmware patches impossible.
- Interestingly, the signature check itself is executed in the bootloader which isn't touched during a firmware upgrade.
EDIT: Here is the re-implementation in python.