Difference between revisions of "SHSH Protocol"

From The iPhone Wiki
Jump to: navigation, search
m (Notes)
Line 176: Line 176:
 
</dict>
 
</dict>
 
</plist>
 
</plist>
In regards to fuzzing and malformed data, the server (as of 2.1.0, at least) seems to have NO bounds checking for the ApNonce, or even checking to see if the ApNonce EXISTS. However, when @APTicket is specified, an ApNonce is obviously used... what is it?
+
In regards to fuzzing and malformed data, the server (as of 2.1.0, at least) seems to have no bounds checking for the ApNonce, or even checking to see if the ApNonce exists. Because the APTicket changes even when specifying an invalid-sized nonce, it is assumed nonces too short are padded, while nonces too large are ignored past the 16th character.
   
 
===Other parameters / open questions===
 
===Other parameters / open questions===

Revision as of 20:11, 22 July 2019

Here is a description about the protocol that is used when iTunes requests the SHSH certificate from Apple. For details about what this is used for, please see the main article SHSH.

This is a simple HTTP (POST) request and answer. You can retry this via a Telnet session or similar. The destination host is gs.apple.com (IP 17.171.36.30 [previously 17.112.176.11]) and runs on the common port 80. The data is plaintext and not encoded in any way. For details about the HTTP protocol itself, please see RFC2616.

Sending data (request)

The request is actually a single XML encoded poperty list. It contains a dictionary which describes the target iOS version, restore behavior etc. Some of the required information can be taken from the BuildManifest.plist. BuildManifest contains some info about the firmware version and "BuildIdentities" dictionary. Inside it you can find two (or more?) build identities. Each of them contains the information which depends on every type of restore ("Erase" or "Update"). Some information is device-dependent (ECID, nonce) and is received from the device.

Request property list should have these properties:

  • @APTicket: Optional boolean value. If true, the server adds an APTicket to the request.
  • @BBTicket: Optional boolean value. The same for the BBTicket.
  • @UDID: Optional string value. Just request UDID. (ex. D9C1F33D-62E0-4D25-8068-F5F46FE80057).
  • @HostPlatformInfo: Optional string value. "mac" for OS X client, "windows" for windows client (can be everything you want).
  • @Locality: Optional string value. Language used for the error response (ex. "en_US");
  • @VersionInfo: Optional string value. The version of 'libauthinstall' used (ex. "libauthinstall-391.0.0.1.3"). You can pass any string here.
  • ApBoardID: Integer value. Board ID of the target device. You can take this value from build manifest.
  • ApChipID: Integer value. Chip ID of the target device. You can take this value from build manifest.
  • ApECID: Integer value. ECID of the target device.
  • ApNonce: Data value. The nonce generated by your device.
  • ApProductionMode: Boolean value. Always true (unless your SOC is PROD 0 - Apple only).
  • ApSecurityDomain: Integer value. The use is unknown. You can take this value from build manifest.
  • UniqueBuildID: Optional data value. This is a unique identifier for every build (different even for "Erase" and "Update" types of restore). You can take this value from build manifest.

The SHSH request also contains information about files from the IPSW (or restore bundle for PurpleRestore). They can be obtained from the 'build identity' you've chosen in the previous step. Each 'build identity' contains a "Manifest" dictionary. It's entries look like

<key>BatteryCharging0</key>
<dict>
	<key>Digest</key>
 	<data>...</data>
	<key>Info</key>
	<dict>...</dict>
 	<key>Trusted<key/>
	<true/>
<dict/>

They are just copied to the SHSH request, but the "Info" dictionary is removed. Example of a SHSH request (... values are identical, *** values are different per device):

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> 
<dict> 
	<key>@APTicket</key> 
	<true /> 
	<key>@BBTicket</key> 
	<true /> 
	<key>@HostPlatformInfo</key> 
	<string>mac</string> 
	<key>@Locality</key> 
	<string>en_US</string> 
	<key>@UUID</key> 
	<string>D9C1F33D-62E0-4D25-8068-F5F46FE80057</string> 
	<key>@VersionInfo</key> 
	<string>libauthinstall-391.0.0.1.3</string> 
	<key>ApBoardID</key> 
	<integer>10</integer>
	<key>ApChipID</key> 
	<integer>35152</integer> 
	<key>ApECID</key> 
	<integer>***</integer> 
	<key>ApNonce</key> 
	<data>***</data> 
	<key>ApProductionMode</key> 
	<true /> 
	<key>ApSecurityDomain</key> 
	<integer>1</integer> 
	<key>UniqueBuildID</key> 
	 fqzW0B++Zdrs+PRwohkwU6prjbk= 
	<key>AppleLogo</key> 
	<dict> 
		<key>Digest</key> 
		<data> uH5QQax5YmIrgcuEHwOhAB//yc8=</data> 
		<key>Trusted</key> 
		<true /> 
	</dict>
	<key>BatteryCharging0</key> 
	<dict> 
		... See AppleLogo dictionary.
	</dict> 
	<key>BatteryCharging1</key> 
	<dict> 
		...
	</dict> 
	<key>BatteryFull</key> 
	<dict> 
		... 
	</dict> 
	<key>BatteryLow0</key> 
	<dict> 
		...
	</dict> 
	<key>BatteryLow1</key> 
	<dict> 
		...
	</dict> 
	<key>BatteryPlugin</key> 
	<dict> 
		...
	</dict> 
	<key>DeviceTree</key> 
	<dict> 
		...
	</dict> 
	<key>KernelCache</key> 
	<dict> 
		...
	</dict> 
	<key>LLB</key> 
	<dict> 
		<key>BuildString</key> 
		<string>iBoot-2817.0.0.1.2~2</string> 
		<key>PartialDigest</key> 
		<data> QAAAAHhRAgC22ZPWJB9J1Nh3H+0XeyRTU72ObA==</data> 
		...
	</dict> 
	<key>RecoveryMode</key> 
	<dict> 
		...
	</dict> 
	<key>RestoreDeviceTree</key> 
	<dict> 
		...
	</dict> 
	<key>RestoreKernelCache</key> 
	<dict> 
		...
	</dict> 
	<key>RestoreLogo</key> 
	<dict> 
		... 
	</dict> 
	<key>RestoreRamDisk</key> 
	<dict> 
		...
	</dict> 
	<key>iBEC</key> 
	<dict> 
		<key>BuildString</key> 
		<string>iBoot-2817.0.0.1.2~2</string> 
		...
	</dict> 
	<key>iBSS</key> 
	<dict> 
		<key>BuildString</key> 
		<string>iBoot-2817.0.0.1.2~2</string> 
		<key>PartialDigest</key> 
		<data> QAAAAHhBAQCRc2PuoresMjW7zNLePMPmX7zaHg==</data> 
		...
	</dict> 
	<key>iBoot</key> 
	<dict> 
		...
	</dict> 
</dict> 
</plist>

Receiving data (answer)

The response is a simple HTTP POST response, containing 3 values: STATUS, MESSAGE (a description string) and REQUEST_STRING (only if the request was successful). STATUS contains numeric status, MESSAGE contains an error message on error and "SUCCESS" string on success and REQUEST_STRING contains response property list encoded as XML.

Status responses

  • STATUS=0&MESSAGE=SUCCESS
  • STATUS=8&MESSAGE=An internal error occurred. (unknown key)
  • STATUS=92&MESSAGE=An internal error occurred. (file too large)
  • STATUS=93&MESSAGE=An internal error occurred. (empty xml data)
  • STATUS=94&MESSAGE=This device isn't eligible for the requested build.
  • STATUS=98&MESSAGE=An internal error occurred. (missing header)
  • STATUS=100&MESSAGE=An internal error occurred. (malformed tags)
  • STATUS=128&MESSAGE=An internal error occurred.
  • STATUS=511&MESSAGE=No data in the request
  • STATUS=551&MESSAGE=Error occured while importing config packet with cpsn:
  • STATUS=5000&MESSAGE=Invalid Option!

Notes

Interestingly, if all you're interested in is the ServerVersion (and a SUCCESS response), sending any one valid Ap-prefixed key will do, like so:

<?xml version="1.0"?>
<plist>
<dict>
        <key>ApSecurityDomain</key>
        <integer>1</integer>
</dict>
</plist>

In regards to fuzzing and malformed data, the server (as of 2.1.0, at least) seems to have no bounds checking for the ApNonce, or even checking to see if the ApNonce exists. Because the APTicket changes even when specifying an invalid-sized nonce, it is assumed nonces too short are padded, while nonces too large are ignored past the 16th character.

Other parameters / open questions

Some parameters could have other values. Not all details are known.

  • action=2 in the request. What other values exist and what is their meaning?
  • ApSecurityDomain Meaning? (Related to SDOM tag)
  • Trusted What is this for?
  • Full description of the above values for UniqueBuildID, Digest, PartialDigest and BuildString.

PartialDigest

The PartialDigest is structured like so:

typedef struct PartialDigest {
    uint32_t transformSize;     // Always 0x40.
    uint32_t img3DataSize;      // data size of the img3.
    uint8_t hash[SHA1HashSize];  // Result of SHA1ResultPartial from the stripped img3.
};

The img3 is first stripped of it's code signature (if present) and then passed through SHA1Update. SHA1ResultPartial is then performed. It is purposely not the final resulting SHA1. The reason for this being, when TSS processes the PartialDigest, it performs one more SHA1Update on the hash to include the ECID before doing SHA1Final and creating the SHSH blob.

Code for SHA1ResultPartial:

int SHA1ResultPartial(SHA1Context* ctx, unsigned char* buf) {
	// Do we have valid buffer and context pointers?
	if(ctx && buf) {
		// Is the Context in good shape?
		if(!ctx->Corrupted && !ctx->Computed && !ctx->Message_Block_Index) {
			for(int i = 0; i < SHA1HashSize; i++) {
				buf[i] = ctx->Intermediate_Hash[i >> 2] >> 8 * (3 - (i & 0x3));
			}
			return 0;
		}
	}
	return 1;
}

Code for tss_strip_img3_signature:

int tss_strip_img3_signature(img3Header* img3, int* len) {
	// Do we have a non-null ptr?
	if(!img3) {
		return 10004;
	}
	// Does the buffer start with the img3 magic?
	if(img3->magic != IMG3_MAGIC) {
		printf("tss_strip_img3_signature: bad magic 0x%08x expecting 0x%08x\n", img3->magic, IMG3_MAGIC);
		return 10004;
	}
	// Sanity checks
	if(img3->shshOffset > img3->dataSize) {
		printf("tss_strip_img3_signature: signed length %u too large for buffer length %u\n", img3->shshOffset, img3->dataSize);
		return 10004;
	}
	// Trim the image by adjusting the header.
	if(img3->shshOffset) {
		*len += img3->shshOffset - img3->dataSize;
		img3->dataSize = img3->shshOffset;
		img3->fullSize = *len;
	}
	return 0;
}

Digest

Digest is the SHA1 of the stripped img3. They are used instead of PartialDigests for the APTicket since the ticket is already personalized for the ECID and all other tags.