Skip to content

Commit

Permalink
Add documentation about low level protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
plietar committed Dec 23, 2015
1 parent 5cbd53f commit a604e9d
Showing 1 changed file with 64 additions and 0 deletions.
64 changes: 64 additions & 0 deletions docs/connection.md
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.

0 comments on commit a604e9d

Please sign in to comment.