mirror of
https://github.com/vacp2p/specs.git
synced 2026-01-09 15:28:03 -05:00
197 lines
15 KiB
Markdown
197 lines
15 KiB
Markdown
# 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](https://datatracker.ietf.org/doc/html/rfc9110). 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 quoted string value the client generates to challenge the server to prove its identity |
|
|
| challenge-client | The random quoted string 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 value 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](https://datatracker.ietf.org/doc/html/rfc9110#name-cmaollected-abnf). Generally it'll be something like: `hostname="example.com", challenge-server="challenge-string"`
|
|
|
|
## Signing
|
|
|
|
Signatures sign some set of parameters prefixed by the string `libp2p-PeerID`. 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="<challenge-string>"` we would first structure the parameters as a byte
|
|
slice containing:
|
|
```
|
|
libp2p-PeerID<varintprefix>challenge-client="<challenge-string>"<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](https://datatracker.ietf.org/doc/html/rfc4648#section-5). Padding MAY be
|
|
omitted. 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 Authentication
|
|
|
|
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 an HTTP request to an authenticated resource.
|
|
2. The server responds with status code 401 (Unauthorized) and set the header:
|
|
```
|
|
WWW-Authenticate: libp2p-PeerID challenge-client="<challenge-string>", 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 HTTP request to the same authenticated resource and sets the header:
|
|
```
|
|
Authorization: libp2p-PeerID peer-id="<string-representation-of-client-peer-id>", opaque="<opaque-from-server>", challenge-server="<challenge-string>"[,encoded-public-key="<base64-encoded-public-key-bytes>" ], sig="<base64-signature-bytes>"
|
|
```
|
|
|
|
The `encoded-public-key` param is optional and represents the client's public key. This is only needed when the client's public key is not included in the PeerID (e.g. not using the "identity" multihash).
|
|
|
|
The `sig` param represents a signature over the parameters:
|
|
- `hostname`
|
|
- `challenge-client`
|
|
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 SHOULD proceed to serve the HTTP request. The server MUST set the following response headers:
|
|
```
|
|
Authentication-Info: libp2p-PeerID peer-id="<server-peer-id-string>", sig="<base64-signature-bytes>", libp2p-Bearer <base64-encoded-opaque-blob>
|
|
```
|
|
The `sig` param represents a signature over the parameters:
|
|
- `hostname`
|
|
- `challenge-server`
|
|
- `client-pubkey` the bytes of the client's public key
|
|
|
|
The `libp2p-Bearer` token allows the client to make future 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.
|
|
|
|
## Security Considerations
|
|
|
|
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.
|
|
|
|
This authentication scheme is also not secure in cases where you do not own your domain name or the certificate. If someone else can get a valid certificate for your domain, you may be vulnerable to 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==", 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) | `todo` |
|
|
| The client's signature | `todo` |
|
|
| The client's Authorization header | `Authorization: libp2p-PeerID peer-id="12D3KooWBtg3aaRMjxwedh83aGiUkwSxDwUZkzuJcfaqUmo7R3pq", opaque="CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=", challenge-server="BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", sig="F5OBYbbMXoIVJNWrW0UANi7rrbj4GCB6kcEceQjajLTMvC-_jpBF9MFlxiaNYXOEiPQqeo_S56YUSNinwl0ZCQ=="` |
|
|
| What the server will sign (percent encoded) | `todo` |
|
|
| The server's signature | `todo` |
|
|
| The server's Authentication-Info header | `Authentication-Info: libp2p-PeerID peer-id="12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN", sig="btLFqW200aDTQqpkKetJJje7V-iDknXygFqPsfiegNsboXeYDiQ6Rqcpezz1wfr8j9h83QkN9z78cAWzKzV_AQ==", libp2p-Bearer <some-opaque-value>` |
|
|
|
|
|
|
[Peer ID spec]: https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md
|
|
|
|
[@MarcoPolo]: https://github.com/MarcoPolo
|
|
[@sukunrt]: https://github.com/sukunrt
|
|
[@achingbrain]: https://github.com/achingbrain
|