librespot/docs/connection.md

67 lines
2.6 KiB
Markdown

# 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.
If `http://apresolve.spotify.com` is unresponsive, `ap.spotify.com:443` is used as a fallback.
## 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.
The client starts by computing the DH shared secret using it's private key and the server's public key.
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.
The 16 bit big endian length only includes the length of the payload.