Files
specs/http/peer-id-auth.md
2024-07-02 14:26:28 -07:00

14 KiB

Peer ID Authentication over HTTP

Lifecycle Stage Maturity Status Latest Revision
1A Working Draft Active r0, 2023-01-23

Authors: @MarcoPolo

Interest Group: @sukunrt, @achingbrain

Introduction

This spec defines an authentication scheme of libp2p Peer IDs in accordance with RFC 9110. The authentication scheme is called libp2p-PeerID.

Protocol Overview

Parameters

Param Name Description
hostname The server name used in the TLS connection (SNI).
challenge-server The random base64 encoded value the client generates to challenge the server to prove its identity
challenge-client The random base64 encoded value the server generates to challenge the client to prove its identity
sig A base64 encoded signature.
peer-id The peer id of the node that set this parameter. Encoding defined by the Peer ID spec.
public-key A base64 encoded value of peer's public key. The key itself is encoded per the Peer ID spec.
opaque An base64 encoded opaque to the client blob generated by the server. If a client receives this it must return it. A server may use this to authenticate statelessly. For example, it could store the challenge-client and a expiry time.

Params are encoded per RFC 9110 auth-param's ABNF. Generally it'll be something like: hostname="example.com", challenge-server=base64EncodedVal

Signing

Signatures sign some set of parameters. The parameters are sorted alphabetically, prepended with a varint length prefix, and concatenated together to form the data to be signed. The signing algorithm is defined by the key type used. Refer to the Peer ID spec for specifics on the signing algorithm. The set of parameters is prefixed with the auth scheme "libp2p-PeerID"

As an example, if we wanted to sign the parameters hostname="example.com", challenge-client=base64String we would first structure the parameters as a byte slice containing:

libp2p-PeerID<varintprefix>challenge-client=<base64String><varintprefix>hostname="example.com"

Then sign the resulting byte slice. See the test vectors below for a examples.

Base64 Encoding

The base64 encoding follows Base 64 Encoding with URL and Filename Safe Alphabet from RFC 4648. The reason this is not a multibase is to aid clients or servers who can not or prefer not to import a multibase dependency.

Mutual Client and Server Peer ID

The following protocol allows both the client and server to authenticate each other's peer id by having them each sign a challenge issued by the other. The protocol operates as follows:

  1. The client makes a POST request to the authentication URI.

  2. The server responds with status code 401 (Unauthorized) and set the header:

    WWW-Authenticate: libp2p-PeerID challenge-client=<base64-encoded-challenge>, opaque=<base64-encoded-opaque-value>
    

    The opaque parameter is opaque to client. The client MUST return the opaque parameter back to the server. The server MAY use the opaque parameter to encode state.

  3. The client makes another POST request to the authentication URI and sets the header:

    Authorization: libp2p-PeerID peer-id=<string-representation-of-client-peer-id>, opaque=<opaque-from-server>, challenge-server=<base64-encoded-challenge-server>, sig=<base64-signature-bytes>
    

    The sig param represents a signature over the parameters:

    • hostname
    • challenge-client in its base64 encoded form.
  4. The server MUST verify the signature using the server name used in the TLS session. The server MUST return 401 Unauthorized if the server fails to validate the signature. If the signature is valid, the server has authenticated the client's peer id. The server MUST respond with status code 200 (OK) and set the headers:

    Authentication-Info: libp2p-PeerID peer-id=<server-peer-id-string>, sig=<base64-signature-bytes>
    Authorization: libp2p-Bearer <base64-encoded-opaque-blob>
    

    The sig param represents a signature over the parameters:

    • hostname
    • challenge-server in its base64 encoded form.
    • client the string representation of the client's Peer ID.

    The libp2p-Bearer token allows the client to make peer id authenticated requests. The value is opaque to the client, and the server may use it to store authentication state such as:

    • The client's Peer ID.
    • The hostname parameter.
    • The token creation date (to allow tokens to expire).
  5. The client MUST verify the signature. After verification the client has authenticated the server's peer id. The client MUST send the libp2p-Bearer token for Peer ID authenticated requests.

libp2p Bearer token

The libp2p Bearer token is a token given to the client from the server that allows the client (the bearer) to make Peer ID authenticated requests to the server. Once the client receives this token after the Mutual Authentication protocol, the client should save it and use it for future authenticated requests.

The server SHOULD return a 401 Unauthorized and follow the above Mutual authentication protocol when it wants the client to request a new libp2p Bearer token.

Authentication URI Endpoint

Because the client needs to make a request to authenticate the server, and the client may not want to make the real request before authenticating the server, the server MAY provide an authentication endpoint. This authentication endpoint is like any other application protocol, and it shows up in .well-known/libp2p/protocols, but it only does the authentication flow. The client and server SHOULD NOT send any data besides what is defined in the above authentication flow. The protocol id for the authentication endpoint is /http-peer-id-auth/1.0.0.

Considerations for Implementations

  • Implementations MUST only authenticate over a secured connection (i.e. TLS).
  • Implementations SHOULD limit the maximum length of any variable length field.

Note on web PKI

Protection against man-in-the-middle (mitm) type attacks is through web PKI. If the client is in an environment where web PKI can not be fully trusted (e.g. an enterprise network with a custom enterprise root CA installed on the client), then this authentication scheme can not protect the client from a mitm attack.

Test Vectors

Definitions used

  • zero key: An ED25519 key initialized with zero bytes.
  • zero peer id: A peer ID derived from the zero key.
  • client key: An ED25519 key with the following marshalled key (refer to the Peer ID spec for how to unmarshal): 080112407e0830617c4a7de83925dfb2694556b12936c477a0e1feb2e148ec9da60fee7d1ed1e8fae2c4a144b8be8fd4b47bf3d3b34b871c3cacf6010f0e42d474fce27e
  • client peer ID: A peer id derived from the client key.

Walkthrough

Included is a concrete example of running the protocol. The client uses the Peer ID defined above, and the server uses the zero key.

  1. The clients sends the initial request.
  2. The server responds with the header:
    WWW-Authenticate: libp2p-PeerID challenge-client=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=, opaque=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
    
  3. The client sends another request with the header:
    Authorization: libp2p-PeerID peer-id=12D3KooWBtg3aaRMjxwedh83aGiUkwSxDwUZkzuJcfaqUmo7R3pq, opaque=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=, challenge-server=BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=, sig=F5OBYbbMXoIVJNWrW0UANi7rrbj4GCB6kcEceQjajLTMvC-_jpBF9MFlxiaNYXOEiPQqeo_S56YUSNinwl0ZCQ==
    
  4. The server responds with the header:
    Authentication-Info: libp2p-PeerID peer-id=12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN, sig=btLFqW200aDTQqpkKetJJje7V-iDknXygFqPsfiegNsboXeYDiQ6Rqcpezz1wfr8j9h83QkN9z78cAWzKzV_AQ==
    Authorization: libp2p-Bearer <base64-encoded-bearer-token>
    

The following table lists out all parameters and intermediate values used in the walkthrough above.

Parameter value
hostname example.com
challenge-client AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
challenge-server BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=
client peer id 12D3KooWBtg3aaRMjxwedh83aGiUkwSxDwUZkzuJcfaqUmo7R3pq
server's peer id The zero key 12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN
The server's opaque blob Could be anything. In this example we'll use CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=.
What the client will sign (percent encoded) libp2p-PeerID=challenge-client=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=%16hostname=%22example.com%22
The client's signature F5OBYbbMXoIVJNWrW0UANi7rrbj4GCB6kcEceQjajLTMvC-_jpBF9MFlxiaNYXOEiPQqeo_S56YUSNinwl0ZCQ==
The client's Authorization header Authorization: libp2p-PeerID peer-id=12D3KooWBtg3aaRMjxwedh83aGiUkwSxDwUZkzuJcfaqUmo7R3pq, opaque=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=, challenge-server=BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=, sig=MKoR8Shzr6VmQ675dErKh_gGGUsGaO8zXnZ8Cx8bIKiQlYBhqazUG8w4lG3_Wd5IfSz5P1HLfXtVb_fg_dsxDw==
What the server will sign (percent encoded) libp2p-PeerID=challenge-server=BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=%3Bclient=12D3KooWBtg3aaRMjxwedh83aGiUkwSxDwUZkzuJcfaqUmo7R3pq%16hostname=%22example.com%22
The server's signature btLFqW200aDTQqpkKetJJje7V-iDknXygFqPsfiegNsboXeYDiQ6Rqcpezz1wfr8j9h83QkN9z78cAWzKzV_AQ==
The server's Authentication-Info header Authentication-Info: libp2p-PeerID peer-id=12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN, sig=m0OkSsO9YGcqfZ_XVTbiRwTtM4ds8434D9aod22Mmo3Wm0vBvxHOd71glC-uEez6g5gjA580KkGc9DOIvP47BQ==