mirror of
https://github.com/librespot-org/librespot.git
synced 2024-11-08 16:45:43 +00:00
Add documentation about low level protocol
This commit is contained in:
parent
5cbd53fef2
commit
a604e9d679
1 changed files with 64 additions and 0 deletions
64
docs/connection.md
Normal file
64
docs/connection.md
Normal file
|
@ -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.
|
||||
|
Loading…
Reference in a new issue