2015-12-23 18:28:11 +00:00
|
|
|
# Connection Setup
|
|
|
|
## Access point Connection
|
|
|
|
The first step to connecting to Spotify's servers is finding an Access Point (AP) to do so.
|
|
|
|
Clients make an HTTP GET request to `http://apresolve.spotify.com` to retrieve a list of hostname an port combination in JSON format.
|
|
|
|
An AP is randomly picked from that list to connect to.
|
|
|
|
|
|
|
|
The connection is done using a bare TCP socket. Despite many APs using ports 80 and 443, neither HTTP nor TLS are used to connect.
|
|
|
|
|
2018-07-03 11:07:29 +00:00
|
|
|
If `http://apresolve.spotify.com` is unresponsive, `ap.spotify.com:443` is used as a fallback.
|
2017-01-13 01:17:30 +00:00
|
|
|
|
2015-12-23 18:28:11 +00:00
|
|
|
## Connection Hello
|
|
|
|
The first 3 packets exchanged are unencrypted, and have the following format :
|
|
|
|
|
|
|
|
header | length | payload
|
|
|
|
---------|--------|---------
|
|
|
|
variable | 32 | variable
|
|
|
|
|
|
|
|
Length is a 32 bit, big endian encoded, integer.
|
|
|
|
It is the length of the entire packet, ie `len(header) + 4 + len(payload)`.
|
|
|
|
|
|
|
|
The header is only present in the very first packet sent by the client, and is two bytes long, `[0, 4]`.
|
|
|
|
It probably corresponds to the protocol version used.
|
|
|
|
|
|
|
|
The payload is a protobuf encoded message.
|
|
|
|
|
|
|
|
The client starts by sending a `ClientHello` message, describing the client info, a random nonce and client's Diffie Hellman public key.
|
|
|
|
|
|
|
|
The AP replies by a `APResponseMessage` message, containing a random nonce and the server's DH key.
|
|
|
|
|
|
|
|
The client solves a challenge based on these two packets, and sends it back using a `ClientResponsePlaintext`.
|
|
|
|
It also computes the shared keys used to encrypt the rest of the communication.
|
|
|
|
|
|
|
|
## Login challenge and cipher key computation.
|
2023-11-18 13:06:49 +00:00
|
|
|
The client starts by computing the DH shared secret using its private key and the server's public key.
|
2015-12-23 18:28:11 +00:00
|
|
|
HMAC-SHA1 is then used to compute the send and receive keys, as well as the login challenge.
|
|
|
|
|
|
|
|
```
|
|
|
|
data = []
|
|
|
|
for i in 1..6 {
|
|
|
|
data += HMAC(client_hello || ap_response || [ i ], shared)
|
|
|
|
}
|
|
|
|
|
|
|
|
challenge = HMAC(client_hello || ap_response, data[:20])
|
|
|
|
send_key = data[20:52]
|
|
|
|
recv_key = data[52:84]
|
|
|
|
```
|
|
|
|
|
|
|
|
`client_hello` and `ap_response` are the first packets sent respectively by the client and the AP.
|
|
|
|
These include the header and length fields.
|
|
|
|
|
|
|
|
## Encrypted packets
|
|
|
|
Every packet after ClientResponsePlaintext is encrypted using a Shannon cipher.
|
|
|
|
|
|
|
|
The cipher is setup with 4 bytes big endian nonce, incremented after each packet, starting at zero.
|
|
|
|
Two independent ciphers and accompanying nonces are used, one for transmission and one for reception,
|
|
|
|
using respectively `send_key` and `recv_key` as keys.
|
|
|
|
|
|
|
|
The packet format is as followed :
|
|
|
|
|
|
|
|
cmd | length | payload | mac
|
|
|
|
----|--------|----------|----
|
|
|
|
8 | 16 | variable | 32
|
|
|
|
|
|
|
|
Each packet has a type identified by the 8 bit `cmd` field.
|
2016-11-03 22:42:54 +00:00
|
|
|
The 16 bit big endian length only includes the length of the payload.
|
2015-12-23 18:28:11 +00:00
|
|
|
|