This commit is contained in:
Marco Munizaga
2024-07-02 14:19:24 -07:00
parent 45006f17d2
commit f56e82d2c5

View File

@@ -6,9 +6,7 @@
Authors: [@MarcoPolo]
[@MarcoPolo]: https://github.com/MarcoPolo
Interest Group: Same as [HTTP](README.md)
Interest Group: [@sukunrt], [@achingbrain]
## Introduction
@@ -22,33 +20,34 @@ scheme is called `libp2p-PeerID`.
| Param Name | Description |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| origin | The server name used in the TLS connection (SNI) |
| origin | 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 | the signature over some set of fields |
| client-peer-id | A client's peer id |
| server-peer-id | A server's peer id |
| public-key | A peer's public key |
| 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](https://datatracker.ietf.org/doc/html/rfc9110#name-collected-abnf). Generally it'll be something like: `origin=example.com, challenge-server=base64EncodedVal`
Params are encoded per [RFC 9110 auth-param's ABNF](https://datatracker.ietf.org/doc/html/rfc9110#name-collected-abnf). Generally it'll be something like: `origin="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 [PeerID
spec](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md) for
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 `origin=example.com,
challenger-server=base64String` we would first structure the parameters as:
As an example, if we wanted to sign the parameters `origin="example.com",
challenge-client=base64String` we would first structure the parameters as a byte
slice containing:
```
libp2p-PeerID<varintprefix>challenge-server=<base64String><varintprefix>origin=example.com
libp2p-PeerID<varintprefix>challenge-client=<base64String><varintprefix>origin="example.com"
```
See the test vectors below for more examples. (todo)
Then sign the resulting byte slice. See the test vectors below for a
examples.
## Base64 Encoding
@@ -58,132 +57,72 @@ from [RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648#section-5). 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 Overview
## Mutual Client and Server Peer ID
1. The client makes a request to the autentication URI.
2. The server responds with the header `WWW-Authenticate: libp2p-PeerID
challenge-client=<base64-encoded-challenge>, opaque=...`. The challenge MUST
be indistinguishable from random data.
3. The client sends a request to the same URI and sets the `Authorization`
[header](https://www.rfc-editor.org/rfc/rfc9110.html#section-11.6.2) header
to the following:
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:
```
libp2p-PeerID peer-id="<encoded-peer-id-bytes>", opaque=... challenge-server="<base64-encoded-challenge-server>", sig="<base64-signature-bytes>"
WWW-Authenticate: libp2p-PeerID challenge-client=<base64-encoded-challenge>, opaque=<base64-encoded-opaque-value>
```
The signature is the client signing the parameters `challenge-client` and `origin`.
4. The server authenticates the signature, and responds by setting the `Authentication-Info` response header to the
following:
```
libp2p-PeerID peer-id="<encoded-peer-id-bytes>",sig="<base64-signature-bytes>"
```
The signature is the server signing the parameters `challenge-server`,
`origin`, and `client` (`client` is the client's string encoded peer id)
5. The client authenticates the signature. At this point both the client and
server have authenticated each other.
## Mutual Client and Server Peer ID Authentication Detailed
(todo reword this)
<!-- 1. The server initiates the authentication by responding to a request that must
be authenticated with the response header `WWW-Authenticate: libp2p-PeerID
challenge-client="<base64-encoded-challenge>`. The challenge MUST be
indistinguishable from random data. The Server MAY randomly generate this
data, or MAY use an server-encrypted value. If using random data the
server SHOULD store the challenge temporarily until the authentication is
done. The challenge SHOULD be at least 32 bytes.
2. The client sends a request and sets the `Authorization`
[header](https://www.rfc-editor.org/rfc/rfc9110.html#section-11.6.2) header
to the following:
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:
```
libp2p-PeerID peer-id="<encoded-peer-id-bytes>",[challenge-server="<base64-encoded-challenge-server>",]sig="<base64-signature-bytes>"]
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 `challenge-server` parameter is optional. The client should set it if
the client wants to authenticate the server.
* The peer-id is encoded per the string encoding described in the [peer-ids spec](../peer-ids/peer-ids.md).
* The signature is over the concatenated result of:
```
<varint-length> + "origin=" + server-name +
[<varint-length> + "challenge-server=" + base64-encoded-client-chosen-challenge-server + ]
<varint-length> + "challenge-client=" + base64-encoded-challenge
```
* Strings are UTF-8 encoded.
* If the challenge server was omitted in the `Authorization` header it MUST
be omitted in the signature.
* If provided, the client-chosen `challenge-server` MUST be randomly generated.
* The client-chosen `challenge-server` SHOULD be at least 32 bytes.
* The client MUST use the same server-name as what is used for the TLS
session.
* If the client _only_ wants to authenticate the server and the server does
not need to authenticate the client, the client can omit the
`challenge-client` from the parameters and signature on its initial request
(since it did not receive a `challenge-client`). If a resource requires
client authentication, the server MUST return `401 Unauthorized` if a
client attempts to authenticate without a `challenge-client`.
* Example on building the message to sign:
```
origin=example.com
client-challenge=qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=
challenge=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
```
Message To Sign in hex with comments
```
12 // (18 bytes)
6f726967696e3d6578616d706c652e636f6d // (origin=example.com)
3d // (61 bytes)
636c69656e742d6368616c6c656e67653d7171717171717171717171717171717171717171717171717171717171717171717171717171717171716f3d // (client-challenge=qq...o=)
36 // (54 bytes)
6368616c6c656e67653d414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d // (challenge=AA...A=)
```
All together:
```
Message To Sign in hex:
126f726967696e3d6578616d706c652e636f6d3d636c69656e742d6368616c6c656e67653d7171717171717171717171717171717171717171717171717171717171717171717171717171717171716f3d366368616c6c656e67653d414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d
```
2. The server MUST verify the signature using the server name used in the TLS
The `sig` param represents a signature over the parameters:
- `origin`
- `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.
3. If the signature is valid, the server has authenticated the client's peer id
and MAY fulfill the request according to application logic. If the request is
fulfilled, the server sets the `Authentication-Info` response header to the
following:
```
libp2p-PeerID peer-id="<encoded-peer-id-bytes>",sig="<base64-signature-bytes>"
```
* The signature is over the concatenated result of:
```
<varint-length> + "origin=" + server-name +
[<varint-length> + "challenge-server=" + base64-encoded-client-chosen-challenge-server + ]
<varint-length> + "client=" + <encoded-client-peer-id-bytes>
```
* Strings are UTF-8 encoded.
* Optionally, the server MAY include a libp2p-Bearer
token in the
`Authentication-Info` response header. This allows clients to avoid a
future iteration of this authentication protocol. If clients see a bearer
token, they SHOULD store it for future use. For example, an
`Authentication-Info` header with a bearer token would look like:
```
libp2p-PeerID peer-id="<encoded-peer-id-bytes>",sig="<base64-signature-bytes>",bearer-token="<token>".
```
4. The client can then authenticate the server with the the signature from
`Authentication-info`. -->
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:
- `origin`
- `challenge-server` in its base64 encoded form.
- `client` the string representation of the client's Peer ID.
## Authentication Endpoint
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 `origin` 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`,
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 flows. The protocol
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`.
@@ -201,5 +140,61 @@ 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=MKoR8Shzr6VmQ675dErKh_gGGUsGaO8zXnZ8Cx8bIKiQlYBhqazUG8w4lG3_Wd5IfSz5P1HLfXtVb_fg_dsxDw==
```
4. The server responds with the header:
```
Authentication-Info: libp2p-PeerID peer-id=12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN, sig=m0OkSsO9YGcqfZ_XVTbiRwTtM4ds8434D9aod22Mmo3Wm0vBvxHOd71glC-uEez6g5gjA580KkGc9DOIvP47BQ==
Authorization: libp2p-Bearer <base64-encoded-bearer-token>
```
The following table lists out all parameters and intermediate values used in the walkthrough above.
| Parameter | value |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| origin | 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=%12origin=%22example.com%22` |
| The client's signature | `MKoR8Shzr6VmQ675dErKh_gGGUsGaO8zXnZ8Cx8bIKiQlYBhqazUG8w4lG3_Wd5IfSz5P1HLfXtVb_fg_dsxDw==` |
| 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%14origin=%22example.com%22` |
| The server's signature | `m0OkSsO9YGcqfZ_XVTbiRwTtM4ds8434D9aod22Mmo3Wm0vBvxHOd71glC-uEez6g5gjA580KkGc9DOIvP47BQ==` |
| The server's Authentication-Info header | `Authentication-Info: libp2p-PeerID peer-id=12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN, sig=m0OkSsO9YGcqfZ_XVTbiRwTtM4ds8434D9aod22Mmo3Wm0vBvxHOd71glC-uEez6g5gjA580KkGc9DOIvP47BQ==` |
TODOS:
- [ ]: Rename origin to hostname
TODO (marco): include a couple examples of what is signed, exchanged, and
resulting signature.
[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