forked from plietar/librespot
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add documentation about low level protocol
- Loading branch information
Showing
1 changed file
with
64 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# 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 inculdes the length of the payload. | ||
|