Creating and Using Bypass Codes
Maintain the bypass code parameters for disabling Activation Lock.
Overview
To manage Activation Lock, your mobile device management (MDM) server implementation needs to store two bypass codes:
The device-generated bypass code. The server retains this code until it receives a different, nonempty code from the device. For more information, see the Activation Lock Bypass Code query.
The bypass code the server creates when initiating Activation Lock through MDM.
The server attempts to unlock the device by passing in an escrow key with one of the bypass codes as its value. Try the other code if the first one fails. It’s impossible for the server to determine which code is active at a given time, or even to determine if the device is in a locked state, because the user can erase the device and remove Activation Lock manually by entering the correct Apple Account or password. The deviceʼs IsActivationLockEnabled value isn’t a reflection of its Activation Lock state because the device can report either a false-positive or a false-negative.
Create a Bypass Code
When initiating Activation Lock for a device, your MDM implementation needs to generate the bypass code necessary to disable Activation Lock at a later time.
The following code sample shows how to generate a bypass code:
#define MCBYPASS_CODE_LENGTH 31 // Excluding terminating null.
#define MCBYPASS_CODE_BUFFER_LENGTH 32 // Including terminating null.
#define MCBYPASS_RAW_BYTES_LENGTH 16
#define MCBYPASS_HASH_LENGTH CC_SHA256_DIGEST_LENGTH
- (NSString*) _createNewActivationLockBypassCodeOutHash:(NSString**)outHash
{
#define RANDOM_BYTES_LENGTH 16
#define SALT_LENGTH 4
// Encode raw bytes.
static const char kSymbols[] = "0123456789ACDEFGHJKLMNPQRTUVWXYZ";
// 00000000000000001111111111111111
// 0123456789abcdef0123456789abcdef
// Insert dashes after outputting characters at these positions.
static const int kDashPositions[] = { 5, 10, 14, 18, 22 };
char rawBytes[MCBYPASS_RAW_BYTES_LENGTH];
char code[MCBYPASS_CODE_BUFFER_LENGTH];
uint8_t hash[MCBYPASS_HASH_LENGTH];
uint8_t salt[SALT_LENGTH] = {0, 0, 0, 0};
arc4random_buf(rawBytes, RANDOM_BYTES_LENGTH);
CCKeyDerivationPBKDF(kCCPBKDF2, rawBytes, RANDOM_BYTES_LENGTH, salt, SALT_LENGTH,
kCCPRFHmacAlgSHA256, 50000, hash, CC_SHA256_DIGEST_LENGTH);
if (outHash) {
int len = MCBYPASS_HASH_LENGTH;
NSMutableString* str = [NSMutableString stringWithCapacity:MCBYPASS_HASH_LENGTH * 2 + 1];
const uint8_t* p = (const uint8_t*)hash;
while (len-- > 0) [str appendFormat:@"%02X", *p++];
*outHash = [NSString stringWithString:str];
}
int outputCharacterCount = 0;
const int* nextDashPosition = kDashPositions;
char* outputCursor = code;
uint8_t* inputCursor = (uint8_t*)rawBytes;
// Generate output one symbol at a time.
#define INPUT_BITS 128
#define BITS_PER_BYTE 8
#define BITS_PER_SYMBOL 5
int bitsProcessed = 0;
int bitOffsetIntoByte = 0;
while (bitsProcessed <= (INPUT_BITS - BITS_PER_SYMBOL)) {
int bitsThisByte = (bitOffsetIntoByte < BITS_PER_BYTE - BITS_PER_SYMBOL
? BITS_PER_SYMBOL : BITS_PER_BYTE - bitOffsetIntoByte);
int bitsNextByte = (bitsThisByte < BITS_PER_SYMBOL ? BITS_PER_SYMBOL
- bitsThisByte : 0);
uint8_t value = (((*inputCursor << bitOffsetIntoByte) & 0xff)
>> (BITS_PER_BYTE - bitsThisByte));
bitOffsetIntoByte += BITS_PER_SYMBOL;
if (bitOffsetIntoByte >= BITS_PER_BYTE) {
bitOffsetIntoByte -= BITS_PER_BYTE;
inputCursor++;
}
if (bitsNextByte) {
value <<= bitsNextByte;
value |= (*inputCursor >> (BITS_PER_BYTE - bitsNextByte));
}
*outputCursor++ = kSymbols[value];
if (++outputCharacterCount == *nextDashPosition) {
++nextDashPosition;
*outputCursor++ = '-';
}
bitsProcessed += BITS_PER_SYMBOL;
}
// Process remaining bits.
int bitsRemaining = INPUT_BITS - bitsProcessed;
if (bitsRemaining) {
uint8_t value = (((*inputCursor << bitOffsetIntoByte) & 0xff)
>> (BITS_PER_BYTE - bitsRemaining));
*outputCursor++ = kSymbols[value];
}
*outputCursor = '\0';
return [NSString stringWithUTF8String:code];
} // -_createNewActivationLockBypassCodeOutHash:
Use a Bypass Code to Disable Activation Lock
To remove Activation Lock, provide the deviceʼs bypass code to the web service as its escrowKey. The request needs to be a standard HTTPS POST on port 443 to https://deviceservices-external.apple.com/deviceservicesworkers/escrowKeyUnlock, and your MDM server must provide its APNs certificate when establishing the SSL connection with the web service. The request must also have application/x-www-form-urlencoded as its contentType header.
You must provide the following arguments as part of the URL request string:
serialThe deviceʼs serial number (required).
imeiThe device’s IMEI (omit for non-cellular devices).
imei2The device’s secondary IMEI (omit for non-cellular and single-SIM devices).
meidThe device’s MEID (omit for non-cellular devices).
productTypeExample: iPad4,1 (required).
You can obtain the IMEI values from the results of a DeviceInformationCommand with a ServiceSubscriptions query. Use the IMEI and MEID values for a Slot value of CTSubscriptionSlotOne for the imei and meid arguments, respectively. For the imei2 value, use the IMEI value (if any) for a Slot value of CTSubscriptionSlotTwo. Note that the server might ignore any MEID value that CTSubscriptionSlotTwo reports—this request only requires the first MEID, even for devices that have two IMEIs.
Include the following arguments in the message body:
orgNameThe client-supplied value for auditing purposes: a string that identifies the name of the organization.
guidThe client-supplied value for auditing purposes: a string that identifies the user requesting the removal (such as email, LDAP ID, or name).
escrowKeyThe device’s bypass code.