librespot/docs/connection.md
2016-11-03 22:44:36 +00:00

2.6 KiB

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.

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.