Just writing here to document some progress and findings.
I was able to do some more digging and reverse engineering some of the information are from the source code package and disassembling u-boot.bin, here's a summary:
- UART is available on S700, 1.8V at 115200 baudrate
- Pressing any key during boot will let you enter fastboot mode
- Fastboot can be accessed via the micro-USB connector and will enumerate as "USB download gadget" with VID 0x1949 PID 0x0320
- Most commands are locked but can be unlocked with the correct unlock signature
- Fastboot command "getvar unlock_code" will give a string based from the device serial number in the format "0xXXXXXXXXXXXXXXXX"
- The unlock signature can be derived from this unlock code with the process: unlock_code -> SHA256 hash -> RSA-PSS signature (2048-bit key, SHA256 digest, 32-byte salt)
- Fastboot command "download <signature_file>" will write the signature to memory
- Fastboot command "flash unlock" will verify the signature and write to IDME if valid
- Once IDME has the correct unlock signature, locked commands can now be accessed
In short, to unlock the restricted fastboot commands, the following process can be used:
Code:
# In this example, assume:
# - The unlock_code to result be: 0x0123456789abcdef
# - RSA-2048 private key file to be: privatekey.pem
$ ./fastboot getvar unlock_code
$ echo -n "0x0123456789abcdef" | openssl dgst -sha256 -binary | openssl pkeyutl -sign -inkey privatekey.pem -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pss -pkeyopt rsa_pss_saltlen:32 > unlock_code.sig
$ ./fastboot download unlock_code.sig
$ ./fastboot flash unlock
Now, the only missing piece of this puzzle is the private key file for signing the unlock code.
A more detailed breakdown:
All fastboot commands that are received by the device goes through is_restricted_command_on_locked_hw() which, as the name suggests, checks if the received command is a restricted command on a locked hardware. It does this by:
1. Calling amzn_target_device_type() to check if the devices OTP fuses are configured for engineering device or production device.
2. If running on a production device, use amzn_target_is_unlocked() to check if locked/unlock by reading the unlock signature from the IDME and calling amzn_verify_unlock() to validate the stored unlock signature.
The function amzn_verify_unlock() is one of the codes that have been redacted from the source code release but I was able to disassemble and decompile this from u-boot.bin with a simplified reconstruction of its implementation below:
Spoiler:
Code:
int amzn_verify_unlock(void *code, unsigned int len)
{
int ret = -1;
unsigned int unlock_key_len = 0;
const unsigned char *unlock_key = amzn_get_unlock_key(&unlock_key_len);
if (!code || !unlock_key || !unlock_key_len)
{
printf("%s: Invalid arg\n", __FUNCTION__);
return -1;
}
rsa_key *key = malloc(sizeof(rsa_key));
if (!key)
{
printf("%s: Cannot malloc key\n", __FUNCTION__);
return -1;
}
ltc_mp = ltm_desc;
if (rsa_import(unlock_key, unlock_key_len, key))
{
printf("%s: Cannot parse key\n", __FUNCTION__);
goto fail;
}
unsigned char unlock_code[32];
unsigned int unlock_code_len = sizeof(unlock_code);
if (amzn_get_unlock_code(unlock_code, &unlock_code_len))
{
printf("%s: Failed to get unlock code\n");
goto fail;
}
register_hash(&sha256_desc);
int hash_idx = find_hash("sha256");
unsigned char hash[32];
unsigned long hash_len = sha256_desc.hashsize;
hash_memory(hash_idx, unlock_code, unlock_code_len, hash, &hash_len);
int stat = 0;
int res = rsa_verify_hash(code, len, hash, hash_len, hash_idx, 32, &stat, key);
if (!res && stat == 1)
{
ret = 0;
}
fail:
rsa_free(key);
free(key);
return ret;
}
Some miscellaneous information I've gathered during this activity:
- Function amzn_verify_unlock() uses LibTomCrypt 1.17 library to perform hash and signature calculations.
- Reset/interrupt vector on u-boot.bin is found on offset 0x402C. Because of this, you will have better disassembly when you load this binary to base address 0x87800000 and file offset 0x402C. Some hardcoded addresses may still be a mess though.
- Available fastboot commands: reboot, getvar, download, boot, continue, flash, erase, oem
- Available fastboot getvar variables: version, bootloader-version, downloadsize or max-download-size, emmc-capacity, serialno, unlock_code, unlock_status, unlock_version
- Available fastboot oem commands: format, relock