Next: Encryption of network packets, Previous: Legacy authentication protocol, Up: Security [Contents][Index]

#### 7.3.2 Simple Peer-to-Peer Security

The SPTPS protocol is designed to address the weaknesses in the legacy protocol. SPTPS is based on TLS 1.2, but has been simplified: there is no support for exchanging public keys, and there is no cipher suite negotiation. Instead, SPTPS always uses a very strong cipher suite: peers authenticate each other using 521 bits ECC keys, Diffie-Hellman using ephemeral 521 bits ECC keys is used to provide perfect forward secrecy (PFS), AES-256-CTR is used for encryption, and HMAC-SHA-256 for message authentication.

Similar to TLS, messages are split up in records. A complete logical record contains the following information:

- uint32_t seqno (network byte order)
- uint16_t length (network byte order)
- uint8_t type
- opaque data[length]
- opaque hmac[HMAC_SIZE] (HMAC over all preceding fields)

Depending on whether SPTPS records are sent via TCP or UDP, either the seqno or the length field is omitted on the wire (but they are still included in the calculation of the HMAC); for TCP packets are guaranteed to arrive in-order so we can infer the seqno, but packets can be split or merged, so we still need the length field to determine the boundaries between records; for UDP packets we know that there is exactly one record per packet, and we know the length of a packet, but packets can be dropped, duplicated and/or reordered, so we need to include the seqno.

The type field is used to distinguish between application records or handshake records. Types 0 to 127 are application records, type 128 is a handshake record, and types 129 to 255 are reserved.

Before the initial handshake, no fields are encrypted, and the HMAC field is not present. After the authentication handshake, the length (if present), type and data fields are encrypted, and the HMAC field is present. For UDP packets, the seqno field is not encrypted, as it is used to determine the value of the counter used for encryption.

The authentication consists of an exchange of Key EXchange, SIGnature and ACKnowledge messages, transmitted using type 128 records.

Overview:

    Initiator   Responder
    ---------------------
    KEX ->
                <- KEX
    SIG ->
                <- SIG

    ...encrypt and HMAC using session keys from now on...

    App ->
                <- App
    ...
                ...

    ...key renegotiation starts here...

    KEX ->
                <- KEX
    SIG ->
                <- SIG
    ACK ->
                <- ACK

    ...encrypt and HMAC using new session keys from now on...

    App ->
                <- App
    ...
                ...
    ---------------------

Note that the responder does not need to wait before it receives the first KEX message, it can immediately send its own once it has accepted an incoming connection.

Key EXchange message:

- uint8_t kex_version (always 0 in this version of SPTPS)
- opaque nonce[32] (random number)
- opaque ecdh_key[ECDH_SIZE]

SIGnature message:

- opaque ecdsa_signature[ECDSA_SIZE]

ACKnowledge message:

- empty (only sent after key renegotiation)

Remarks:

- At the start, both peers generate a random nonce and an Elliptic Curve public key and send it to the other in the KEX message.
- After receiving the other’s KEX message, both KEX messages are concatenated (see below), and the result is signed using ECDSA. The result is sent to the other.
- After receiving the other’s SIG message, the signature is verified. If it is correct, the shared secret is calculated from the public keys exchanged in the KEX message using the Elliptic Curve Diffie-Helman algorithm.
- The shared secret key is expanded using a PRF. Both nonces and the application specific label are also used as input for the PRF.
- An ACK message is sent only when doing key renegotiation, and is sent using the old encryption keys.
- The expanded key is used to key the encryption and HMAC algorithms.

The signature is calculated over this string:

- uint8_t initiator (0 = local peer, 1 = remote peer is initiator)
- opaque remote_kex_message[1 + 32 + ECDH_SIZE]
- opaque local_kex_message[1 + 32 + ECDH_SIZE]
- opaque label[label_length]

The PRF is calculated as follows:

- A HMAC using SHA512 is used, the shared secret is used as the key.
- For each block of 64 bytes, a HMAC is calculated. For block n: hmac[n] = HMAC_SHA512(hmac[n - 1] + seed)
- For the first block (n = 1), hmac[0] is given by HMAC_SHA512(zeroes + seed), where zeroes is a block of 64 zero bytes.

The seed is as follows:

- const char[13] "key expansion"
- opaque responder_nonce[32]
- opaque initiator_nonce[32]
- opaque label[label_length]

The expanded key is used as follows:

- opaque responder_cipher_key[CIPHER_KEYSIZE]
- opaque responder_digest_key[DIGEST_KEYSIZE]
- opaque initiator_cipher_key[CIPHER_KEYSIZE]
- opaque initiator_digest_key[DIGEST_KEYSIZE]

Where initiator_cipher_key is the key used by session initiator to encrypt messages sent to the responder.

When using 521 bits EC keys, the AES-256-CTR cipher and HMAC-SHA-256 digest algorithm, the sizes are as follows:

    ECDH_SIZE:       67 (= ceil(521/8) + 1)
    ECDSA_SIZE:     141 (= 2 * ceil(521/8) + 9)
    CIPHER_KEYSIZE:  48 (= 256/8 + 128/8)
    DIGEST_KEYSIZE:  32 (= 256/8)

Note that the cipher key also includes the initial value for the counter.

Next: Encryption of network packets, Previous: Legacy authentication protocol, Up: Security [Contents][Index]