The iPhone Wiki is no longer updated. Visit this article on The Apple Wiki for current information. |
Difference between revisions of "SHSH Protocol"
m (→Other parameters / open questions: Case-sensitivity police on duty) |
(→Status responses) |
||
(47 intermediate revisions by 12 users not shown) | |||
Line 1: | Line 1: | ||
− | Here is a description |
+ | Here is a description of 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 [[wikipedia:Hypertext Transfer Protocol|HTTP]] ([[wikipedia:POST (HTTP)|POST]]) request and answer. You can retry this via a [[wikipedia:Telnet|Telnet]] session or similar. The destination host is gs.apple.com |
+ | This is a simple [[wikipedia:Hypertext Transfer Protocol|HTTP]] ([[wikipedia:POST (HTTP)|POST]]) request and answer. You can retry this via a [[wikipedia:Telnet|Telnet]] session or similar. The destination host is [http://gs.apple.com/TSS/controller?action=2 gs.apple.com] and runs on the common [[wikipedia:TCP and UDP port|port]] 80. The data is plaintext and not encoded in any way. For details about the [[wikipedia:Hypertext Transfer Protocol|HTTP]] protocol itself, please see [http://www.w3.org/Protocols/HTTP/1.1/rfc2616.pdf RFC2616]. |
+ | |||
+ | == Communication == |
||
+ | |||
+ | Reliable communication to the TSS server is best achieved with the following HTTP header configuration: |
||
+ | |||
+ | * <code>Proxy-Connection: Keep-Alive</code> |
||
+ | * <code>Pragma: no-cache</code> |
||
+ | * <code>Content-Type: text/xml; charset="utf-8"</code> |
||
+ | |||
+ | The following [https://curl.haxx.se/ curl] command demonstrates a command-line request to the server: |
||
+ | |||
+ | curl -i -s -m 1.0 -X POST -A "InetURL/1.0" -H "Proxy-Connection: Keep=Alive" -H "Pragma: no-cache" -H "Content-Type: text/xml; charset=\"utf-8\"" -d @/location/to/TSSRequest.xml gs.apple.com/TSS/controller?action=2 |
||
+ | |||
+ | ===Action types=== |
||
+ | Attached to the http://gs.apple.com/TSS/controller URL is commonly <code>?action=2</code>, which can be discovered when performing an iTunes restore and observing packets sent with a packet sniffer such as [https://www.wireshark.org Wireshark]. However, values other than 2 may produce different server responses, of which all known value/response pairs have been listed below. |
||
+ | |||
+ | * <code>?action=0</code>: Performs a "Tatsu Signing Server HealthCheck", returning an HTML body of "Server not ready". This may be a general ping protocol for ensuring the TSS service is online. |
||
+ | * <code>?action=2</code>: Common SHSH protocol. See [[#Status responses]] for all known server responses. |
||
+ | * <code>?action=3</code>: Functionally the same as <code>?action=2</code>. |
||
+ | * <code>?action=5</code>: Generates <code>respCode</code> and <code>respMsg</code>. This response syntax differs slightly from the SHSH protocol. The name "cpsn" is referenced. |
||
+ | |||
+ | All unlisted action types appear to respond without HTML content, only status. |
||
===Sending data (request)=== |
===Sending data (request)=== |
||
+ | The request is actually a single XML-encoded property 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. |
||
− | POST /TSS/controller?action=2 HTTP/1.1 |
||
+ | |||
− | Accept: */* |
||
+ | Request property list should have these properties: |
||
− | Cache-Control: no-cache |
||
+ | |||
− | Content-type: text/xml; charset="utf-8" |
||
+ | * '''@APTicket:''' Optional boolean value. If true, the server adds an [[APTicket]] to the request. |
||
− | User-Agent: InetURL/1.0 |
||
+ | * '''@BBTicket:''' Optional boolean value. The same for the [[BBTicket]]. |
||
− | Content-Length: 12345 |
||
+ | * '''@HostPlatformInfo:''' Optional string value. "mac" for OS X client, "windows" for Windows client (can be anything you want). |
||
− | Host: gs.apple.com |
||
+ | * '''@Locality:''' Optional string value. Language used for the error response (ex. "en_US"). |
||
− | |||
+ | * '''@UUID:''' Optional string value. Just a random request UUID (ex. D9C1F33D-62E0-4D25-8068-F5F46FE80057). |
||
− | (here comes the XML request file) |
||
+ | * '''@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. [[BORD |Board ID]] of the target device. You can take this value from the build manifest. |
||
+ | * '''ApChipID:''' Integer value. [[CHIP|Chip ID]] of the target device. You can take this value from the 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 the 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 the 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> |
||
+ | <nowiki><data>...</data></nowiki> |
||
+ | <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>[[BORD |ApBoardID]]</key> |
||
+ | <integer>10</integer> |
||
+ | <key>[[CHIP |ApChipID]]</key> |
||
+ | <integer>35152</integer> |
||
+ | <key>[[ECID |ApECID]]</key> |
||
+ | <integer>***</integer> |
||
+ | <key>[[Nonce |ApNonce]]</key> |
||
+ | <nowiki><data>***</data> </nowiki> |
||
+ | <key>ApProductionMode</key> |
||
+ | <true /> |
||
+ | <key>ApSecurityDomain</key> |
||
+ | <integer>1</integer> |
||
+ | <key>UniqueBuildID</key> |
||
+ | <data> fqzW0B++Zdrs+PRwohkwU6prjbk=</data> |
||
+ | <key>AppleLogo</key> |
||
+ | <dict> |
||
+ | <key>Digest</key> |
||
+ | <nowiki><data> uH5QQax5YmIrgcuEHwOhAB//yc8=</data> </nowiki> |
||
+ | <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> |
||
+ | <nowiki><data> QAAAAHhRAgC22ZPWJB9J1Nh3H+0XeyRTU72ObA==</data> </nowiki> |
||
+ | ... |
||
+ | </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> |
||
+ | <nowiki><data> QAAAAHhBAQCRc2PuoresMjW7zNLePMPmX7zaHg==</data> </nowiki> |
||
+ | ... |
||
+ | </dict> |
||
+ | <key>[[iBoot]]</key> |
||
+ | <dict> |
||
+ | ... |
||
+ | </dict> |
||
+ | </dict> |
||
+ | </plist> |
||
===Receiving data (answer)=== |
===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. |
||
− | HTTP/1.1 200 OK |
||
− | Date: Sun, 15 Aug 2010 19:25:18 GMT |
||
− | Server: Apache-Coyote/1.1 |
||
− | X-Powered-By: Servlet 2.4; JBoss-4.0.5.GA (build: CVSTag=Branch_4_0 date=200610162339)/Tomcat-5.5 |
||
− | Content-Type: text/html |
||
− | Content-Length: 123456 |
||
− | MS-Author-Via: DAV |
||
− | |||
− | STATUS=0&MESSAGE=SUCCESS&REQUEST_STRING=(here comes the requested [[SHSH]] file) |
||
− | === |
+ | ===Status responses=== |
+ | *'''STATUS=0&MESSAGE=SUCCESS''' |
||
− | As you can see, this is a simple XML file. Within <dict> there is always a <key> and then a value. |
||
+ | ** The request was sent successfully, and a response was returned.''' |
||
+ | *'''STATUS=8&MESSAGE=An internal error occurred.''' |
||
+ | ** A key contained an unknown name. |
||
+ | *'''STATUS=69&MESSAGE=This device isn't eligible for the requested build.''' |
||
+ | *'''STATUS=83&MESSAGE=An internal error occurred.''' |
||
+ | ** A key was assigned an invalid value type. |
||
+ | *'''STATUS=92&MESSAGE=An internal error occurred.''' |
||
+ | ** The request contained more than 65536 bytes. |
||
+ | *'''STATUS=93&MESSAGE=An internal error occurred.''' |
||
+ | ** The request contained less than 10 bytes. |
||
+ | *'''STATUS=94&MESSAGE=This device isn't eligible for the requested build.''' |
||
+ | ** The specified build is either unsigned or nonexistent, or a property was requested which the server declined to authorize (example: DPRO and DSEC values of 1, EPRO and ESEC values of 0). |
||
+ | *'''STATUS=98&MESSAGE=An internal error occurred.''' |
||
+ | ** The XML/PList identification tag is missing or the start of the tag is malformed. |
||
+ | *'''STATUS=100&MESSAGE=An internal error occurred.''' |
||
+ | ** A general XML error was found: the XML/PList version is nonexistent or not "1.0", the end of the XML/PList identification tag is malformed, an unknown or empty tag was found, the root <code><plist></code> tag is missing, no keys were found, a value tag contained an invalid type, an unescaped ampersand character was found, two consecutive dash characters were found in a comment, or a tag was found without its counterpart. |
||
+ | *'''STATUS=103&MESSAGE=An internal error occurred.''' |
||
+ | ** Multiple '@' characters were found at the beginning of a key name. |
||
+ | *'''STATUS=128&MESSAGE=An internal error occurred.''' |
||
+ | *'''STATUS=131&MESSAGE=An internal error occurred.''' |
||
+ | ** ''TBD: Something to do with <code>@APTicket</code>?'' |
||
+ | *'''STATUS=132&MESSAGE=An internal error occurred.''' |
||
+ | *'''STATUS=133&MESSAGE=An internal error occurred.''' |
||
+ | ** The <code>BbGoldCertId</code> key is missing in a Baseband install. |
||
+ | *'''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=== |
||
− | <?xml version="1.0" encoding="UTF-8"?> |
||
+ | * 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: |
||
− | <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "<nowiki>http://www.apple.com/DTDs/PropertyList-1.0.dtd</nowiki>"> |
||
+ | <?xml version="1.0"?> |
||
− | |||
− | <plist |
+ | <plist> |
<dict> |
<dict> |
||
− | <key> |
+ | <key>ApSecurityDomain</key> |
+ | <integer>1</integer> |
||
− | <true /> |
||
+ | </dict> |
||
− | <key>@BBTicket</key> |
||
− | <true /> |
||
− | <key>@HostIpAddress</key> |
||
− | <string>192.168.0.1</string> |
||
− | <key>@HostPlatformInfo</key> |
||
− | <string>windows</string> |
||
− | <key>@Locality</key> |
||
− | <string>en_US</string> |
||
− | <key>@VersionInfo</key> |
||
− | <string>libauthinstall-34</string> |
||
− | <key>ApBoardID</key> |
||
− | <integer>2</integer> |
||
− | <key>ApChipID</key> |
||
− | <integer>12345</integer> |
||
− | <key>ApECID</key> |
||
− | <string>1234567890123</string> |
||
− | <key>ApProductionMode</key> |
||
− | <true /> |
||
− | <key>ApSecurityDomain</key> |
||
− | <integer>1</integer> |
||
− | <key>UniqueBuildID</key> |
||
− | <data>123412341234123412341234123=</data> |
||
− | <key>AppleLogo</key> |
||
− | <dict> |
||
− | <key>Digest</key> |
||
− | <data>123412341234123412341234123=</data> |
||
− | <key>PartialDigest</key> |
||
− | <data>12341234123412341234123412341234123412==</data> |
||
− | <key>Trusted</key> |
||
− | <true /> |
||
− | </dict> |
||
− | The <key><dict> tags repeat here. |
||
</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. 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. |
||
− | In the above request, the <key><dict> tags repeat for: |
||
− | *AppleLogo |
||
− | *BatteryCharging |
||
− | *BatteryCharging0 |
||
− | *BatteryCharging1 |
||
− | *BatteryFull |
||
− | *BatteryLow0 |
||
− | *BatteryLow1 |
||
− | *BatteryPlugin |
||
− | *DeviceTree |
||
− | *KernelCache |
||
− | *LLB (*) |
||
− | *RecoveryMode |
||
− | *RestoreDeviceTree |
||
− | *RestoreKernelCache |
||
− | *RestoreLogo |
||
− | *RestoreRamDisk |
||
− | *iBEC (*) |
||
− | *iBSS (*) |
||
− | *iBoot |
||
− | The requested <key><dict> values may depend on the firmware version, because we need a certificate for each part. |
||
+ | * Most textual characters between tags are ignored. For example, inserting non-bracket text (excluding XML special characters, such as '&') after a <code><dict></code> and before the following <code><key></code> will have no effect. |
||
− | All these <key> values have a <dict> block with the tags Digest, PartialDigest, Trusted. Only exception are the marked (*) values: These have a dict block with the tags BuildString and PartialDigest only. The BuildString always contains the value "iBoot-889.24~4" in my test request for all three keys. |
||
+ | * A version string in the XML/Plist identification tag is '''required''', or a <code>STATUS=100</code> internal error message will result. Any encoding string provided is ignored and defaulted to (presumably) "UTF-8". |
||
− | Infos about the needed values: |
||
+ | |||
− | *ApChipID: 5 digit number - probably platform ID. |
||
+ | * The DOCTYPE tag is completely ignored. As such, specifying any non-PList DTD has no effect. |
||
− | *ApECID: This is the [[ECID]] in decimal format. |
||
− | *UniqueBuildID: (unknown) [[wikipedia:Base64|Base64]] encoded |
||
− | *Digest: (unknown) [[wikipedia:Base64|Base64]] encoded |
||
− | *PartialDigest: (unknown) [[wikipedia:Base64|Base64]] encoded |
||
===Other parameters / open questions=== |
===Other parameters / open questions=== |
||
Some parameters could have other values. Not all details are known. |
Some parameters could have other values. Not all details are known. |
||
+ | *ApSecurityDomain: Meaning? (Related to [[SDOM]] tag) |
||
− | *action=2 in the request. What other values exist and what is their meaning? |
||
+ | *Trusted: What is this for? |
||
− | *STATUS=0&MESSAGE=SUCCESS in the answer. What other values exist? |
||
− | *@APTicket can have other values than true? What is this for? |
||
− | *@BBTicket can have other values than true? What is this for? |
||
− | *@HostIpAddress This was not my IP address, so it is assumed this will not be checked. |
||
− | *@HostPlatformInfo What would this value be on a Mac? |
||
− | *@Locality This will probably not be checked. This test request was from outside US. |
||
− | *@VersionInfo Are other values in use? |
||
− | *ApBoardID Do values other than 2 exist? Where can this value be read? |
||
− | *ApProductionMode What does this mean? Is there a test environment? |
||
− | *ApSecurityDomain Meaning? |
||
− | *Trusted What is this for? |
||
*Full description of the above values for UniqueBuildID, Digest, PartialDigest and BuildString. |
*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. |
||
[[Category:Firmware Tags]] |
[[Category:Firmware Tags]] |
||
− | [[Category:Firmware Parsing]] |
Latest revision as of 00:21, 3 January 2023
Here is a description of 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 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.
Contents
Communication
Reliable communication to the TSS server is best achieved with the following HTTP header configuration:
Proxy-Connection: Keep-Alive
Pragma: no-cache
Content-Type: text/xml; charset="utf-8"
The following curl command demonstrates a command-line request to the server:
curl -i -s -m 1.0 -X POST -A "InetURL/1.0" -H "Proxy-Connection: Keep=Alive" -H "Pragma: no-cache" -H "Content-Type: text/xml; charset=\"utf-8\"" -d @/location/to/TSSRequest.xml gs.apple.com/TSS/controller?action=2
Action types
Attached to the http://gs.apple.com/TSS/controller URL is commonly ?action=2
, which can be discovered when performing an iTunes restore and observing packets sent with a packet sniffer such as Wireshark. However, values other than 2 may produce different server responses, of which all known value/response pairs have been listed below.
?action=0
: Performs a "Tatsu Signing Server HealthCheck", returning an HTML body of "Server not ready". This may be a general ping protocol for ensuring the TSS service is online.?action=2
: Common SHSH protocol. See #Status responses for all known server responses.?action=3
: Functionally the same as?action=2
.?action=5
: GeneratesrespCode
andrespMsg
. This response syntax differs slightly from the SHSH protocol. The name "cpsn" is referenced.
All unlisted action types appear to respond without HTML content, only status.
Sending data (request)
The request is actually a single XML-encoded property 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.
- @HostPlatformInfo: Optional string value. "mac" for OS X client, "windows" for Windows client (can be anything you want).
- @Locality: Optional string value. Language used for the error response (ex. "en_US").
- @UUID: Optional string value. Just a random request UUID (ex. D9C1F33D-62E0-4D25-8068-F5F46FE80057).
- @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 the build manifest.
- ApChipID: Integer value. Chip ID of the target device. You can take this value from the 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 the 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 the 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
- The request was sent successfully, and a response was returned.
- STATUS=8&MESSAGE=An internal error occurred.
- A key contained an unknown name.
- STATUS=69&MESSAGE=This device isn't eligible for the requested build.
- STATUS=83&MESSAGE=An internal error occurred.
- A key was assigned an invalid value type.
- STATUS=92&MESSAGE=An internal error occurred.
- The request contained more than 65536 bytes.
- STATUS=93&MESSAGE=An internal error occurred.
- The request contained less than 10 bytes.
- STATUS=94&MESSAGE=This device isn't eligible for the requested build.
- The specified build is either unsigned or nonexistent, or a property was requested which the server declined to authorize (example: DPRO and DSEC values of 1, EPRO and ESEC values of 0).
- STATUS=98&MESSAGE=An internal error occurred.
- The XML/PList identification tag is missing or the start of the tag is malformed.
- STATUS=100&MESSAGE=An internal error occurred.
- A general XML error was found: the XML/PList version is nonexistent or not "1.0", the end of the XML/PList identification tag is malformed, an unknown or empty tag was found, the root
<plist>
tag is missing, no keys were found, a value tag contained an invalid type, an unescaped ampersand character was found, two consecutive dash characters were found in a comment, or a tag was found without its counterpart.
- A general XML error was found: the XML/PList version is nonexistent or not "1.0", the end of the XML/PList identification tag is malformed, an unknown or empty tag was found, the root
- STATUS=103&MESSAGE=An internal error occurred.
- Multiple '@' characters were found at the beginning of a key name.
- STATUS=128&MESSAGE=An internal error occurred.
- STATUS=131&MESSAGE=An internal error occurred.
- TBD: Something to do with
@APTicket
?
- TBD: Something to do with
- STATUS=132&MESSAGE=An internal error occurred.
- STATUS=133&MESSAGE=An internal error occurred.
- The
BbGoldCertId
key is missing in a Baseband install.
- The
- 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.
- Most textual characters between tags are ignored. For example, inserting non-bracket text (excluding XML special characters, such as '&') after a
<dict>
and before the following<key>
will have no effect.
- A version string in the XML/Plist identification tag is required, or a
STATUS=100
internal error message will result. Any encoding string provided is ignored and defaulted to (presumably) "UTF-8".
- The DOCTYPE tag is completely ignored. As such, specifying any non-PList DTD has no effect.
Other parameters / open questions
Some parameters could have other values. Not all details are known.
- 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.