From e3460e239ff036999036501088ea98ba43fd1e54 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Wed, 2 Aug 2023 14:27:11 -0700 Subject: [PATCH 01/23] Add http peer id auth --- http/peer-id-auth.md | 101 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 http/peer-id-auth.md diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md new file mode 100644 index 000000000..27772b9db --- /dev/null +++ b/http/peer-id-auth.md @@ -0,0 +1,101 @@ +# Peer ID Authentication over HTTP + +| Lifecycle Stage | Maturity | Status | Latest Revision | +| --------------- | ------------- | ------ | --------------- | +| 1A | Working Draft | Active | r0, 2023-01-23 | + +Authors: [@MarcoPolo] + +Interest Group: Same as [HTTP](README.md) + +## Introduction + +This spec defines one way of authenticating Peer IDs over HTTP using a +challenge-response scheme. + +## Mutual Client and Server Peer ID Authentication + +1. The server initiates the authentication by responding to a request that must + be authenticated with the response header `WWW-Authenticate: Libp2p-Challenge + challenge=", Libp2p-Challenge-Server-Only"`. The + challenge MUST be randomly generated from server for sole purpose of + authenticating the client. The server SHOULD store the challenge temporarily + until the authentication is done. The challenge SHOULD be at least 32 bytes. +1. 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: + ``` + Libp2p-Challenge peer-id="",client-challenge="",sig="" + ``` + * The peer-id is encoded per the [peer-ids spec](../peer-ids/peer-ids.md). + * The signature is over the concatenated result of: + ``` + + "origin=" + server-name + + + "client-challenge=" + base64-encoded-client-chosen-client-challenge + + + "challenge=" + base64-encoded-challenge + ``` + * The client chosen client-challenge MUST be randomly generated. + * The client chosen client-challenge SHOULD be at least 32 bytes. + * The client MUST use the same server-name as what is used for the TLS + session. +1. 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. +1. 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-Challenge peer-id="",sig="" + ``` + * The signature is over the concatenated result of: + ``` + + "origin=" + server-name + + + "client-challenge=" + base64-encoded-client-chosen-client-challenge + + + "client=" + + ``` +1. The client can then authenticate the server with the the signature from + `Authentication-info`. + +## Server Authentication + +Clients may wish to only authenticate the server's peer ID, but not themselves. +For example, a short lived client may want to get a block from a specific peer. + +The protocol to do so is as follows: + +1. The client should set the request header `Authorization` to + `Libp2p-Challenge-Server-Only `. +1. The server should response to the request and set `Authentication-Info` + response header to the following: + ``` + Libp2p-Challenge-Server-Only peer-id="",sig="" + ``` + * The signature is over the concatenated result of: + ``` + + "origin=" + server-name + + + "client-challenge=" + base64-encoded-client-chosen-client-challenge + ``` +1. The client can now authenticate the server. + +## Authentication 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`, +but it only does the authentication flow. It doesn’t send any other data besides +what is defined in the above authentication flows. The protocol id for the +authentication endpoint is `/http-peer-id-auth/1.0.0`. + + +## Considerations for Implementations + +* 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. From 4af03c4fe7523f32ad2ab5cb59aa512da138a731 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Fri, 4 Aug 2023 14:52:58 -0700 Subject: [PATCH 02/23] Include message to sign example. Add bearer token info --- http/peer-id-auth.md | 55 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 27772b9db..014edbfec 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -6,6 +6,8 @@ Authors: [@MarcoPolo] +[@MarcoPolo]: https://github.com/MarcoPolo + Interest Group: Same as [HTTP](README.md) ## Introduction @@ -34,14 +36,39 @@ challenge-response scheme. + "client-challenge=" + base64-encoded-client-chosen-client-challenge + + "challenge=" + base64-encoded-challenge ``` - * The client chosen client-challenge MUST be randomly generated. - * The client chosen client-challenge SHOULD be at least 32 bytes. + * Strings are UTF-8 encoded. + * The client-chosen client-challenge MUST be randomly generated. + * The client-chosen client-challenge SHOULD be at least 32 bytes. * The client MUST use the same server-name as what is used for the TLS session. -1. The server MUST verify the signature using the server name used in the TLS + * Example: + ``` + 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 session. The server MUST return 401 Unauthorized if the server fails to validate the signature. -1. If the signature is valid, the server has authenticated the client's peer id +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: @@ -54,7 +81,17 @@ challenge-response scheme. + "client-challenge=" + base64-encoded-client-chosen-client-challenge + + "client=" + ``` -1. The client can then authenticate the server with the the signature from + * Strings are UTF-8 encoded. + * Optionally, the server MAY include a [Bearer + token](https://datatracker.ietf.org/doc/html/rfc6750) 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-Challenge peer-id="",sig="",bearer-token="". + ``` +4. The client can then authenticate the server with the the signature from `Authentication-info`. ## Server Authentication @@ -76,6 +113,7 @@ The protocol to do so is as follows: + "origin=" + server-name + + "client-challenge=" + base64-encoded-client-chosen-client-challenge ``` + * The same notes from mutual authentication apply here as well. 1. The client can now authenticate the server. ## Authentication Endpoint @@ -84,13 +122,14 @@ 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`, -but it only does the authentication flow. It doesn’t send any other data besides -what is defined in the above authentication flows. The protocol id for the -authentication endpoint is `/http-peer-id-auth/1.0.0`. +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 +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 From 43a1c721cc9448141e5f365d584b4a0a70d5c01a Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 7 Aug 2023 16:44:46 -0700 Subject: [PATCH 03/23] A single auth scheme --- http/peer-id-auth.md | 64 +++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 014edbfec..8dd2d2265 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -13,35 +13,47 @@ Interest Group: Same as [HTTP](README.md) ## Introduction This spec defines one way of authenticating Peer IDs over HTTP using a -challenge-response scheme. +challenge-response scheme. The authentication scheme is called `Libp2p-PeerID`` ## Mutual Client and Server Peer ID Authentication 1. The server initiates the authentication by responding to a request that must - be authenticated with the response header `WWW-Authenticate: Libp2p-Challenge - challenge=", Libp2p-Challenge-Server-Only"`. The - challenge MUST be randomly generated from server for sole purpose of - authenticating the client. The server SHOULD store the challenge temporarily - until the authentication is done. The challenge SHOULD be at least 32 bytes. + be authenticated with the response header `WWW-Authenticate: Libp2p-PeerID + challenge-client="`. The challenge MUST be randomly + generated from server for sole purpose of authenticating the client. The + server SHOULD store the challenge temporarily until the authentication is + done. The challenge SHOULD be at least 32 bytes. + 1. 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: ``` - Libp2p-Challenge peer-id="",client-challenge="",sig="" + Libp2p-PeerID peer-id="",[challenge-server="",]sig=""] ``` + + * 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 [peer-ids spec](../peer-ids/peer-ids.md). * The signature is over the concatenated result of: ``` + "origin=" + server-name + - + "client-challenge=" + base64-encoded-client-chosen-client-challenge + - + "challenge=" + base64-encoded-challenge + [ + "challenge-server=" + base64-encoded-client-chosen-challenge-server + ] + + "challenge-client=" + base64-encoded-challenge ``` * Strings are UTF-8 encoded. - * The client-chosen client-challenge MUST be randomly generated. - * The client-chosen client-challenge SHOULD be at least 32 bytes. + * 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. - * Example: + * 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= @@ -73,12 +85,12 @@ challenge-response scheme. fulfilled, the server sets the `Authentication-Info` response header to the following: ``` - Libp2p-Challenge peer-id="",sig="" + Libp2p-PeerID peer-id="",sig="" ``` * The signature is over the concatenated result of: ``` + "origin=" + server-name + - + "client-challenge=" + base64-encoded-client-chosen-client-challenge + + [ + "challenge-server=" + base64-encoded-client-chosen-challenge-server + ] + "client=" + ``` * Strings are UTF-8 encoded. @@ -89,33 +101,11 @@ challenge-response scheme. token, they SHOULD store it for future use. For example, an `Authentication-Info` header with a bearer token would look like: ``` - Libp2p-Challenge peer-id="",sig="",bearer-token="". + Libp2p-PeerID peer-id="",sig="",bearer-token="". ``` 4. The client can then authenticate the server with the the signature from `Authentication-info`. -## Server Authentication - -Clients may wish to only authenticate the server's peer ID, but not themselves. -For example, a short lived client may want to get a block from a specific peer. - -The protocol to do so is as follows: - -1. The client should set the request header `Authorization` to - `Libp2p-Challenge-Server-Only `. -1. The server should response to the request and set `Authentication-Info` - response header to the following: - ``` - Libp2p-Challenge-Server-Only peer-id="",sig="" - ``` - * The signature is over the concatenated result of: - ``` - + "origin=" + server-name + - + "client-challenge=" + base64-encoded-client-chosen-client-challenge - ``` - * The same notes from mutual authentication apply here as well. -1. The client can now authenticate the server. - ## Authentication Endpoint Because the client needs to make a request to authenticate the server, and the From abd08f2ed536aa9d6311c0fc3093d1f97fbabe59 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 7 Aug 2023 16:53:53 -0700 Subject: [PATCH 04/23] Allow for using an server-encrypted value as challenge-client --- http/peer-id-auth.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 8dd2d2265..26a5ff884 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -19,8 +19,9 @@ challenge-response scheme. The authentication scheme is called `Libp2p-PeerID`` 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="`. The challenge MUST be randomly - generated from server for sole purpose of authenticating the client. The + challenge-client="`. 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. From 7bfd2aef7cb6439ac31d5ab288b77660540b4229 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Tue, 8 Aug 2023 12:27:52 -0700 Subject: [PATCH 05/23] PR comments --- http/peer-id-auth.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 26a5ff884..031c6249a 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -12,8 +12,9 @@ Interest Group: Same as [HTTP](README.md) ## Introduction -This spec defines one way of authenticating Peer IDs over HTTP using a -challenge-response scheme. The authentication scheme is called `Libp2p-PeerID`` +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`. ## Mutual Client and Server Peer ID Authentication @@ -44,7 +45,7 @@ challenge-response scheme. The authentication scheme is called `Libp2p-PeerID`` * 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. + * 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. @@ -129,3 +130,8 @@ 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 + +TODO (marco): include a couple examples of what is signed, exchanged, and +resulting signature. From 6c733c49250b1dd4127814e21e4395533a8be2a5 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Tue, 8 Aug 2023 12:31:58 -0700 Subject: [PATCH 06/23] Wordsmithing --- http/peer-id-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 031c6249a..4e984f185 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -35,7 +35,7 @@ scheme is called `Libp2p-PeerID`. * 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 [peer-ids spec](../peer-ids/peer-ids.md). + * 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: ``` + "origin=" + server-name + From 1f1d05ccb97c56e20b3b976491172b101470aa6c Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Sat, 29 Jun 2024 12:19:19 -0700 Subject: [PATCH 07/23] Add overview, add parameter table --- http/peer-id-auth.md | 90 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 4e984f185..f68985fd8 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -14,23 +14,91 @@ Interest Group: Same as [HTTP](README.md) 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`. +scheme is called `libp2p-PeerID`. -## Mutual Client and Server Peer ID Authentication +## Protocol Overview + +## Parameters + +| Param Name | Description | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 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 | +| 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` + +## 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 +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: +``` +libp2p-PeerIDchallenge-server=origin=example.com +``` + +See the test vectors below for more examples. (todo) + + +## 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). 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 + +1. The client makes a request to the autentication URI. +2. The server responds with the header `WWW-Authenticate: libp2p-PeerID + challenge-client=, 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: + ``` + libp2p-PeerID peer-id="", opaque=... challenge-server="", sig="" + ``` + 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="",sig="" + ``` + 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 + be authenticated with the response header `WWW-Authenticate: libp2p-PeerID challenge-client="`. 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. -1. The client sends a request and sets the `Authorization` +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: ``` - Libp2p-PeerID peer-id="",[challenge-server="",]sig=""] + libp2p-PeerID peer-id="",[challenge-server="",]sig=""] ``` * The `challenge-server` parameter is optional. The client should set it if @@ -38,7 +106,7 @@ scheme is called `Libp2p-PeerID`. * 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: ``` - + "origin=" + server-name + + + "origin=" + server-name + [ + "challenge-server=" + base64-encoded-client-chosen-challenge-server + ] + "challenge-client=" + base64-encoded-challenge ``` @@ -87,23 +155,23 @@ scheme is called `Libp2p-PeerID`. fulfilled, the server sets the `Authentication-Info` response header to the following: ``` - Libp2p-PeerID peer-id="",sig="" + libp2p-PeerID peer-id="",sig="" ``` * The signature is over the concatenated result of: ``` - + "origin=" + server-name + + + "origin=" + server-name + [ + "challenge-server=" + base64-encoded-client-chosen-challenge-server + ] + "client=" + ``` * Strings are UTF-8 encoded. - * Optionally, the server MAY include a [Bearer - token](https://datatracker.ietf.org/doc/html/rfc6750) in the + * 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="",sig="",bearer-token="". + libp2p-PeerID peer-id="",sig="",bearer-token="". ``` 4. The client can then authenticate the server with the the signature from `Authentication-info`. From 45006f17d2fa0cede50b2db2311a55061011a3fc Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Sat, 29 Jun 2024 12:20:45 -0700 Subject: [PATCH 08/23] Comment out parts I want to reword --- http/peer-id-auth.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index f68985fd8..8198c55cc 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -86,7 +86,7 @@ prefer not to import a multibase dependency. (todo reword this) -1. The server initiates the authentication by responding to a request that must + ## Authentication Endpoint From f56e82d2c57a7136ece10a1bce5e6007fc8dc5ce Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Tue, 2 Jul 2024 14:19:24 -0700 Subject: [PATCH 09/23] Reword --- http/peer-id-auth.md | 251 +++++++++++++++++++++---------------------- 1 file changed, 123 insertions(+), 128 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 8198c55cc..ca7740479 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -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-PeerIDchallenge-server=origin=example.com +libp2p-PeerIDchallenge-client=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=, 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="", opaque=... challenge-server="", sig="" + WWW-Authenticate: libp2p-PeerID challenge-client=, opaque= ``` - 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="",sig="" - ``` - 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) - - - -## Authentication Endpoint + 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. + + 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 + ``` + + +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 From ccec98035ebe5bda90d4dd32932651ce0338f238 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Tue, 2 Jul 2024 14:26:28 -0700 Subject: [PATCH 10/23] Rename origin to hostname --- http/peer-id-auth.md | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index ca7740479..4444f7408 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -20,7 +20,7 @@ scheme is called `libp2p-PeerID`. | Param Name | Description | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| origin | The server name used in the TLS connection (SNI). | +| 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. | @@ -28,7 +28,7 @@ scheme is called `libp2p-PeerID`. | 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: `hostname="example.com", challenge-server=base64EncodedVal` ## Signing @@ -39,11 +39,11 @@ 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", +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-PeerIDchallenge-client=origin="example.com" +libp2p-PeerIDchallenge-client=hostname="example.com" ``` Then sign the resulting byte slice. See the test vectors below for a @@ -77,7 +77,7 @@ protocol operates as follows: ``` The `sig` param represents a signature over the parameters: - - `origin` + - `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 @@ -89,7 +89,7 @@ protocol operates as follows: Authorization: libp2p-Bearer ``` The `sig` param represents a signature over the parameters: - - `origin` + - `hostname` - `challenge-server` in its base64 encoded form. - `client` the string representation of the client's Peer ID. @@ -97,7 +97,7 @@ protocol operates as follows: 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 `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` @@ -160,11 +160,11 @@ Included is a concrete example of running the protocol. The client uses the Peer ``` 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== + 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=m0OkSsO9YGcqfZ_XVTbiRwTtM4ds8434D9aod22Mmo3Wm0vBvxHOd71glC-uEez6g5gjA580KkGc9DOIvP47BQ== + Authentication-Info: libp2p-PeerID peer-id=12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN, sig=btLFqW200aDTQqpkKetJJje7V-iDknXygFqPsfiegNsboXeYDiQ6Rqcpezz1wfr8j9h83QkN9z78cAWzKzV_AQ== Authorization: libp2p-Bearer ``` @@ -173,26 +173,20 @@ The following table lists out all parameters and intermediate values used in the | Parameter | value | | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| origin | example.com | +| 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=%12origin=%22example.com%22` | -| The client's signature | `MKoR8Shzr6VmQ675dErKh_gGGUsGaO8zXnZ8Cx8bIKiQlYBhqazUG8w4lG3_Wd5IfSz5P1HLfXtVb_fg_dsxDw==` | +| 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%14origin=%22example.com%22` | -| The server's signature | `m0OkSsO9YGcqfZ_XVTbiRwTtM4ds8434D9aod22Mmo3Wm0vBvxHOd71glC-uEez6g5gjA580KkGc9DOIvP47BQ==` | +| 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==` | -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 From f97e5966f67ca9fc133fd1dbfb014923e7748891 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 8 Jul 2024 14:56:44 -0700 Subject: [PATCH 11/23] Fix table --- http/peer-id-auth.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 4444f7408..3969bb043 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -181,10 +181,10 @@ The following table lists out all parameters and intermediate values used in the | 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==` | +| 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) | `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==` | +| The server's Authentication-Info header | `Authentication-Info: libp2p-PeerID peer-id=12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN, sig=btLFqW200aDTQqpkKetJJje7V-iDknXygFqPsfiegNsboXeYDiQ6Rqcpezz1wfr8j9h83QkN9z78cAWzKzV_AQ==` | [Peer ID spec]: https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md From a1091a4371122e5c7d6dd44e127adfc0aae18609 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Wed, 21 Aug 2024 12:41:50 -0700 Subject: [PATCH 12/23] Address PR comments --- http/peer-id-auth.md | 114 ++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 56 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 3969bb043..d8542e86f 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -18,21 +18,21 @@ scheme is called `libp2p-PeerID`. ## 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](https://datatracker.ietf.org/doc/html/rfc9110#name-collected-abnf). Generally it'll be something like: `hostname="example.com", challenge-server=base64EncodedVal` +| 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. The parameters are sorted +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 @@ -40,10 +40,10 @@ 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 +challenge-client=""` we would first structure the parameters as a byte slice containing: ``` -libp2p-PeerIDchallenge-client=hostname="example.com" +libp2p-PeerIDchallenge-client=""hostname="example.com" ``` Then sign the resulting byte slice. See the test vectors below for a @@ -52,55 +52,56 @@ 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). The -reason this is not a multibase is to aid clients or servers who can not or -prefer not to import a multibase dependency. +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 +## 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 +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. +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=, opaque= + WWW-Authenticate: libp2p-PeerID challenge-client="", opaque="" ``` 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: +3. The client makes another HTTP request to the same authenticated resource and sets the header: ``` - Authorization: libp2p-PeerID peer-id=, opaque=, challenge-server=, sig= + Authorization: libp2p-PeerID peer-id="", opaque="", challenge-server=""[,encoded-public-key="" ], sig="" ``` + 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` in its base64 encoded form. + - `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 MUST respond with status code - 200 (OK) and set the headers: + 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=, sig= - Authorization: libp2p-Bearer + Authentication-Info: libp2p-PeerID peer-id="", sig="", libp2p-Bearer ``` 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. + - `challenge-server` + - `client-pubkey` the bytes of the client's public key - The `libp2p-Bearer` token allows the client to make peer id authenticated + 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` + authenticated the server's Peer ID. The client MUST send the `libp2p-Bearer` token for Peer ID authenticated requests. ## libp2p Bearer token @@ -131,21 +132,23 @@ id for the authentication endpoint is `/http-peer-id-auth/1.0.0`. * 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 +## 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 +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. +- 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. +- client Peer ID: A Peer ID derived from the client key. ### Walkthrough @@ -156,35 +159,34 @@ Included is a concrete example of running the protocol. The client uses the Peer 1. The clients sends the initial request. 2. The server responds with the header: ``` - WWW-Authenticate: libp2p-PeerID challenge-client=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=, opaque=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + 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== + 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 + Authentication-Info: libp2p-PeerID peer-id="12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN", sig="btLFqW200aDTQqpkKetJJje7V-iDknXygFqPsfiegNsboXeYDiQ6Rqcpezz1wfr8j9h83QkN9z78cAWzKzV_AQ==", libp2p-Bearer ``` 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=F5OBYbbMXoIVJNWrW0UANi7rrbj4GCB6kcEceQjajLTMvC-_jpBF9MFlxiaNYXOEiPQqeo_S56YUSNinwl0ZCQ==` | -| 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=btLFqW200aDTQqpkKetJJje7V-iDknXygFqPsfiegNsboXeYDiQ6Rqcpezz1wfr8j9h83QkN9z78cAWzKzV_AQ==` | +| 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 ` | [Peer ID spec]: https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md From d5ec85ae2d9d796b220509541c9e79ca07518faf Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 26 Aug 2024 17:43:29 -0700 Subject: [PATCH 13/23] Wordsmithing --- http/peer-id-auth.md | 55 +++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index d8542e86f..88c3e928f 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -18,15 +18,15 @@ scheme is called `libp2p-PeerID`. ## 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. | +| 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 | A 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"` @@ -74,10 +74,10 @@ protocol operates as follows: encode state. 3. The client makes another HTTP request to the same authenticated resource and sets the header: ``` - Authorization: libp2p-PeerID peer-id="", opaque="", challenge-server=""[,encoded-public-key="" ], sig="" + Authorization: libp2p-PeerID peer-id="", opaque="", challenge-server="",[ public-key="",] sig="" ``` - 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 `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` @@ -87,34 +87,41 @@ protocol operates as follows: 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="", sig="", libp2p-Bearer + Authentication-Info: libp2p-PeerID peer-id="", sig=""[, public-key=""] bearer="" ``` + + Again, the `public-key` param is optional and represents the server's public key. This is only needed when the 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-server` - - `client-pubkey` the bytes of the client's public key + - `client-peer-id` the string representation fo the client's peer id - The `libp2p-Bearer` token allows the client to make future Peer ID authenticated + The `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` + authenticated the server's Peer ID. The client SHOULD send the `bearer` token for Peer ID authenticated requests. -## libp2p Bearer token +## libp2p bearer token -The libp2p Bearer token is a token given to the client from the server that +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. +server. Once the client receives this token, they 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 protocol when it wants the client to request a new libp2p +bearer token. + +To use the bearer token, the client MUST set the Authorization header as follows: +``` +Authorization: libp2p-PeerID bearer="" +``` ## Authentication URI Endpoint @@ -167,7 +174,7 @@ Included is a concrete example of running the protocol. The client uses the Peer ``` 4. The server responds with the header: ``` - Authentication-Info: libp2p-PeerID peer-id="12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN", sig="btLFqW200aDTQqpkKetJJje7V-iDknXygFqPsfiegNsboXeYDiQ6Rqcpezz1wfr8j9h83QkN9z78cAWzKzV_AQ==", libp2p-Bearer + Authentication-Info: libp2p-PeerID peer-id="12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN", sig="btLFqW200aDTQqpkKetJJje7V-iDknXygFqPsfiegNsboXeYDiQ6Rqcpezz1wfr8j9h83QkN9z78cAWzKzV_AQ==", bearer="" ``` @@ -186,7 +193,7 @@ The following table lists out all parameters and intermediate values used in the | 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 ` | +| The server's Authentication-Info header | `Authentication-Info: libp2p-PeerID peer-id="12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN", sig="btLFqW200aDTQqpkKetJJje7V-iDknXygFqPsfiegNsboXeYDiQ6Rqcpezz1wfr8j9h83QkN9z78cAWzKzV_AQ==", bearer=""` | [Peer ID spec]: https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md From 1d35258ec16296391dd2130aa7818bec3cad5e3b Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Tue, 27 Aug 2024 10:59:14 -0700 Subject: [PATCH 14/23] Drop peer-id parameter. Only public keys --- http/peer-id-auth.md | 58 +++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 88c3e928f..9cd11ca6a 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -24,8 +24,7 @@ scheme is called `libp2p-PeerID`. | 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]. | +| public-key | A base64 encoded value of peer's public key. This MUST be the key used for the Peer's Peer ID. The key itself is encoded per the [Peer ID spec]. | | opaque | A 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"` @@ -39,11 +38,11 @@ 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=""` we would first structure the parameters as a byte +As an example, if we wanted to sign the parameters `hostname = example.com`, +`challenge-client = `, and `client-public-key = 0xbadcofee` we would first structure the parameters as a byte slice containing: ``` -libp2p-PeerIDchallenge-client=""hostname="example.com" +libp2p-PeerIDchallenge-client=hostname=example.comclient-public-key= ``` Then sign the resulting byte slice. See the test vectors below for a @@ -58,6 +57,13 @@ Alphabet from [RFC 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. +## Public Key Encoding + +The authentication below exchanges the peer's public key instead of its PeerID, +as the public key can be used to verify signatures and derive the PeerID, while +the PeerID may not contain the public key in all cases. The Public Key is +encoded per the [Peer ID spec] under the section "Keys" section. + ## Mutual Client and Server Peer ID Authentication The following protocol allows both the client and server to authenticate each @@ -67,35 +73,31 @@ 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="", opaque="" + WWW-Authenticate: libp2p-PeerID challenge-client="", opaque="" ``` 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="", opaque="", challenge-server="",[ public-key="",] sig="" + Authorization: libp2p-PeerID public-key="", opaque="", challenge-server="", sig="" ``` - The `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: + authenticated the client's public key, and thus its PeerID. The server SHOULD proceed to serve the HTTP request. The server MUST set the following response headers: ``` - Authentication-Info: libp2p-PeerID peer-id="", sig=""[, public-key=""] bearer="" + Authentication-Info: libp2p-PeerID public-key="", sig="" bearer="" ``` - Again, the `public-key` param is optional and represents the server's public key. This is only needed when the 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-server` - - `client-peer-id` the string representation fo the client's peer id + - `client-public-key` the bytes of the client's public-key encoded per the [Peer ID spec]. The `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 @@ -180,20 +182,20 @@ Included is a concrete example of running the protocol. The client uses the Peer 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==", bearer=""` | +| 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 | `todo` | +| What the server will sign (percent encoded) | `todo` | +| The server's signature | `todo` | +| The server's Authentication-Info header | `todo` | [Peer ID spec]: https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md From 24ef2bb3bef3161fef6a0a50a7034823e799329e Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Tue, 27 Aug 2024 11:00:24 -0700 Subject: [PATCH 15/23] Formatting --- http/peer-id-auth.md | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 9cd11ca6a..9e2a43a6c 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -60,8 +60,7 @@ can not or prefer not to import a multibase dependency. ## Public Key Encoding The authentication below exchanges the peer's public key instead of its PeerID, -as the public key can be used to verify signatures and derive the PeerID, while -the PeerID may not contain the public key in all cases. The Public Key is +as the PeerID alone may not be enough to validate a signature. The Public Key is encoded per the [Peer ID spec] under the section "Keys" section. ## Mutual Client and Server Peer ID Authentication @@ -78,7 +77,9 @@ protocol operates as follows: 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: +3. The client makes another HTTP request to the same authenticated resource and + sets the header: + ``` Authorization: libp2p-PeerID public-key="", opaque="", challenge-server="", sig="" ``` @@ -89,7 +90,9 @@ protocol operates as follows: 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 public key, and thus its PeerID. The server SHOULD proceed to serve the HTTP request. The server MUST set the following response headers: + authenticated the client's public key, and thus its PeerID. The server SHOULD + proceed to serve the HTTP request. The server MUST set the following response + headers: ``` Authentication-Info: libp2p-PeerID public-key="", sig="" bearer="" ``` @@ -148,7 +151,9 @@ 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. +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 @@ -156,14 +161,15 @@ This authentication scheme is also not secure in cases where you do not own your - 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 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. - - +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: @@ -179,7 +185,6 @@ Included is a concrete example of running the protocol. The client uses the Peer Authentication-Info: libp2p-PeerID peer-id="12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN", sig="btLFqW200aDTQqpkKetJJje7V-iDknXygFqPsfiegNsboXeYDiQ6Rqcpezz1wfr8j9h83QkN9z78cAWzKzV_AQ==", bearer="" ``` - The following table lists out all parameters and intermediate values used in the walkthrough above. | Parameter | value | From c000bb3bcfc7b90357b273724d2654b0e06ed410 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Tue, 27 Aug 2024 18:01:02 -0700 Subject: [PATCH 16/23] Sort parameters to sign --- http/peer-id-auth.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 9e2a43a6c..dd56b7fc0 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -85,8 +85,8 @@ protocol operates as follows: ``` The `sig` param represents a signature over the parameters: - - `hostname` - `challenge-client` + - `hostname` 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 @@ -98,9 +98,9 @@ protocol operates as follows: ``` The `sig` param represents a signature over the parameters: - - `hostname` - `challenge-server` - `client-public-key` the bytes of the client's public-key encoded per the [Peer ID spec]. + - `hostname` The `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 From e1df5078d1c99a3436c116549657fc3380650a73 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Wed, 28 Aug 2024 13:07:38 -0700 Subject: [PATCH 17/23] Fill in examples --- http/peer-id-auth.md | 107 ++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 62 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index dd56b7fc0..4cf865201 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -33,21 +33,25 @@ Params are encoded per [RFC 9110 auth-param's ABNF](https://datatracker.ietf.org 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 = `, and `client-public-key = 0xbadcofee` we would first structure the parameters as a byte -slice containing: -``` -libp2p-PeerIDchallenge-client=hostname=example.comclient-public-key= -``` - -Then sign the resulting byte slice. See the test vectors below for a -examples. - +to form the data to be signed. The parameter name and value is split with a `=`. +If the parameter value is appended directly after the `=`. Strings MUST be UTF-8 +encoded. Byte Arrays MUST be appended as-is. 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" + +### Signing Example +| Parameter | Value | +| ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| hostname | example.com | +| Server Private Key (pb encoded as hex) | 0801124001010101010101010101010101010101010101010101010101010101010101018a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c | +| challenge-server | ERERERERERERERERERERERERERERERERERERERERERE= | +| Client Public Key (pb encoded as hex) | 080112208139770ea87d175f56a35466c34c7ecccb8d8a91b4ee37a25df60f5b8fc9b394 | +| data to sign ([percent encoded](https://datatracker.ietf.org/doc/html/rfc3986#section-2.1)) | libp2p-PeerID=challenge-server=ERERERERERERERERERERERERERERERERERERERERERE=6client-public-key=%08%01%12%20%819w%0E%A8%7D%17_V%A3Tf%C3L~%CC%CB%8D%8A%91%B4%EE7%A2%5D%F6%0F%5B%8F%C9%B3%94%14hostname=example.com | +| data to sign (hex encoded) | 6c69627032702d5065657249443d6368616c6c656e67652d7365727665723d455245524552455245524552455245524552455245524552455245524552455245524552455245524552453d36636c69656e742d7075626c69632d6b65793d080112208139770ea87d175f56a35466c34c7ecccb8d8a91b4ee37a25df60f5b8fc9b39414686f73746e616d653d6578616d706c652e636f6d | +| signature (base64 encoded) | UA88qZbLUzmAxrD9KECbDCgSKAUBAvBHrOCF2X0uPLR1uUCF7qGfLPc7dw3Olo-LaFCDpk5sXN7TkLWPVvuXAA== | + +Note that the `=` after the libp2p-PeerID scheme is actually the varint length of the challenge-server parameter. ## Base64 Encoding @@ -155,53 +159,32 @@ 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==", bearer="" - ``` - -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 | `todo` | -| What the server will sign (percent encoded) | `todo` | -| The server's signature | `todo` | -| The server's Authentication-Info header | `todo` | - +## Complete Example Handshake + +### Parameters +| Parameter | Value | +| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| hostname | example.com | +| Server Private Key (pb encoded as hex) | 0801124001010101010101010101010101010101010101010101010101010101010101018a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c | +| Server HMAC Key (hex) | 0000000000000000000000000000000000000000000000000000000000000000 | +| Challenge Client | ERERERERERERERERERERERERERERERERERERERERERE= | +| Client Private Key (pb encoded as hex) | 0801124002020202020202020202020202020202020202020202020202020202020202028139770ea87d175f56a35466c34c7ecccb8d8a91b4ee37a25df60f5b8fc9b394 | +| Challenge Server | MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMz | +| "Now" time | 1969-12-31 16:00:00 -0800 PST | + +### Handshake Diagram +```mermaid +sequenceDiagram +Client->>Server: Initial request +Server->>Client: WWW-Authenticate=libp2p-PeerID challenge-client="ERERERERERERERERERERERERERERERERERERERERERE=", opaque="0H1Y9sq1zrfTJZCCTcTymI2tV_TF9-PzdMip2dFkiqZ7ImNoYWxsZW5nZS1jbGllbnQiOiJFUkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFPSIsImhvc3RuYW1lIjoiZXhhbXBsZS5jb20iLCJjcmVhdGVkLXRpbWUiOiIxOTY5LTEyLTMxVDE2OjAwOjAwLTA4OjAwIn0=" +Client->>Server: Authorization=libp2p-PeerID public-key="CAESIIE5dw6ofRdfVqNUZsNMfszLjYqRtO43ol32D1uPybOU", challenge-server="MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMz", sig="5RT0BbFdn-hMgE4pQ_GH9tnlKpptGUQZvkh8kVLbwy81Rzli_vfiNOsuGTcMk8lyUfkmTFmk79b5XUZCR3-RBw==", opaque="0H1Y9sq1zrfTJZCCTcTymI2tV_TF9-PzdMip2dFkiqZ7ImNoYWxsZW5nZS1jbGllbnQiOiJFUkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFPSIsImhvc3RuYW1lIjoiZXhhbXBsZS5jb20iLCJjcmVhdGVkLXRpbWUiOiIxOTY5LTEyLTMxVDE2OjAwOjAwLTA4OjAwIn0=" +Note left of Server: Server has authenticated Client +Server->>Client: Authentication-Info=libp2p-PeerID sig="HQ7BJRaSpRhNCORNiALNJENdwXUyq0eM2cxNoxe-XnQw6oEAMaeYnjMYaHHjgq0XNxZmy4W2ngKUcI1CgprLCQ==", bearer="YhlYjHWTMOkTleROtjMiChL7Mx15_GDYfi971mdJCqB7ImlzLXRva2VuIjp0cnVlLCJwZWVyLWlkIjoiMTJEM0tvb1dKV29hcVpoRGFvRUZzaEY3UmgxYnBZOW9oaWhGaHpjVzZkNjlMcjJOQVN1cSIsImhvc3RuYW1lIjoiZXhhbXBsZS5jb20iLCJjcmVhdGVkLXRpbWUiOiIxOTY5LTEyLTMxVDE2OjAwOjAwLTA4OjAwIn0=", public-key="CAESIIqI4910CfGV_VLbLTy6XXLKZwm_HZQSG_N0iAG0D29c" +Note right of Client: Client has authenticated Server + +Note over Client: Future requests use the bearer token +Client->>Server: Authorization=libp2p-PeerID bearer="YhlYjHWTMOkTleROtjMiChL7Mx15_GDYfi971mdJCqB7ImlzLXRva2VuIjp0cnVlLCJwZWVyLWlkIjoiMTJEM0tvb1dKV29hcVpoRGFvRUZzaEY3UmgxYnBZOW9oaWhGaHpjVzZkNjlMcjJOQVN1cSIsImhvc3RuYW1lIjoiZXhhbXBsZS5jb20iLCJjcmVhdGVkLXRpbWUiOiIxOTY5LTEyLTMxVDE2OjAwOjAwLTA4OjAwIn0=" +``` [Peer ID spec]: https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md From 189492a0d7a2c3019eca18dcacdd3a72e83a15b1 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Tue, 3 Sep 2024 15:47:11 -0700 Subject: [PATCH 18/23] Nits --- http/peer-id-auth.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 4cf865201..b378dc269 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -161,6 +161,14 @@ your domain, you may be vulnerable to a mitm attack. ## Complete Example Handshake +The following is a complete and reproducible handshake. Generated by the current +implementation of this spec in go-libp2p. + +Understanding the opaque value is not necessary in order to understand this +spec. Servers are free to do whatever they want with the opaque field. The +opaque value represents encoded server state authenticated with an HMAC. The +details can be found in the go-libp2p source. + ### Parameters | Parameter | Value | | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | @@ -170,7 +178,7 @@ your domain, you may be vulnerable to a mitm attack. | Challenge Client | ERERERERERERERERERERERERERERERERERERERERERE= | | Client Private Key (pb encoded as hex) | 0801124002020202020202020202020202020202020202020202020202020202020202028139770ea87d175f56a35466c34c7ecccb8d8a91b4ee37a25df60f5b8fc9b394 | | Challenge Server | MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMz | -| "Now" time | 1969-12-31 16:00:00 -0800 PST | +| "Now" time | 1970-01-01 00:00:00 +0000 UTC | ### Handshake Diagram ```mermaid From 05012d7a480b0c2c2a1c5d7030653ccf163af81e Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Thu, 5 Sep 2024 14:33:28 -0700 Subject: [PATCH 19/23] Add diagram in overview --- http/peer-id-auth.md | 59 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index b378dc269..356475819 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -10,12 +10,65 @@ 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`. +This spec defines an HTTP 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 +At a high level, challenges are exchanged and signed by each peer to +authenticate themselves to each other. The protocol works whether the Client +provides the first challenge, or the Server provides the first challenge. + +Example Diagram of Server initiated handshake +``` +┌─────────┐ ┌────────┐ +│ Client │ │ Server │ +└─────────┘ └────────┘ + │ initial request │ + ├────────────────────────────>│ + │ │ + │ 401; challenge-client │ + │<────────────────────────────┤ + │ │ + │ client-sig + │ + │ challenge-server │ + │ [client authenticated] │ + ├────────────────────────────>│ + │ │ + │ server-sig │ + │ [server authenticated] │ + │<────────────────────────────┤ + │ │ + │ application data │ + ├────────────────────────────>│ + │ │ + │ resp │ + │<────────────────────────────┤ +``` + +Example Diagram of Client initiated handshake +``` +┌────────┐ ┌────────┐ +│ Client │ │ Server │ +└────────┘ └────────┘ + │ challenge-server │ + ├────────────────────────────>│ + │ │ + │ challenge-client + │ + │ server-sig │ + │ [server authenticated] │ + │<────────────────────────────┤ + │ │ + │ client-sig + │ + │ application data │ + │ [client authenticated] │ + ├────────────────────────────>│ + │ │ + │ resp │ + │<────────────────────────────┤ +``` + ## Parameters | Param Name | Description | From 8753236829e231a4e661731885453d1e743884c7 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Thu, 5 Sep 2024 15:22:47 -0700 Subject: [PATCH 20/23] Add Client Initiated handshake --- http/peer-id-auth.md | 83 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 356475819..165478228 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -126,14 +126,22 @@ 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: +### Server Initiated Handshake + 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="", opaque="" + WWW-Authenticate: libp2p-PeerID challenge-client="", public-key="", opaque="" ``` + + The public-key parameter is the server's public key. It is the same public + key used to derive the server's peer id. + 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: @@ -141,17 +149,22 @@ protocol operates as follows: Authorization: libp2p-PeerID public-key="", opaque="", challenge-server="", sig="" ``` + The public-key parameter is the client's public key. It is the same public + key used to derive the client's peer id. + The `sig` param represents a signature over the parameters: - `challenge-client` + - `server-public-key` the bytes of the server's public-key encoded per the [Peer ID spec]. - `hostname` -4. The server MUST verify the signature using the server name used in the TLS + +4. The server SHOULD 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 public key, and thus its PeerID. The server SHOULD proceed to serve the HTTP request. The server MUST set the following response headers: ``` - Authentication-Info: libp2p-PeerID public-key="", sig="" bearer="" + Authentication-Info: libp2p-PeerID, sig="" bearer="" ``` The `sig` param represents a signature over the parameters: @@ -165,10 +178,72 @@ protocol operates as follows: - 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 SHOULD send the `bearer` token for Peer ID authenticated requests. +### Client Initiated Handshake + +The client initiated version of this handshake follows the same structure, +except that the client sends initially sends a `challenge-server` and the order +of who is authenticated first is reversed. The protocol is as follows + +1. The client makes an HTTP request to a known authenticated resource and sets + the header: + + ``` + Authorization: libp2p-PeerID challenge-server="", public-key="" + ``` + +2. The server responds with status code 401 (Unauthorized) and set the header: + ``` + WWW-Authenticate: libp2p-PeerID challenge-client="", opaque="", public-key="", sig="" + ``` + + The `sig` param represents a signature over the parameters: + - `challenge-server` + - `client-public-key` the bytes of the client's public-key encoded per the [Peer ID spec]. + - `hostname` + +3. The client MUST verify the signature. After verification the client has + authenticated the server's Peer ID. + + The client makes another HTTP request to the same authenticated resource and + sets the header: + + ``` + Authorization: libp2p-PeerID opaque="", sig="" + ``` + + The client MAY send application data in this request. + + The `sig` param represents a signature over the parameters: + - `challenge-client` + - `server-public-key` the bytes of the server's public-key encoded per the [Peer ID spec]. + - `hostname` + +4. The server MUST verify the signature. The server SHOULD 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 public key, and thus its + PeerID. The server SHOULD proceed to serve the HTTP request. The server MUST + set the following response headers: + ``` + Authentication-Info: libp2p-PeerID bearer="" + ``` + + The `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 SHOULD send the `bearer` token for future Peer ID authenticated + requests. + + ## libp2p bearer token The libp2p bearer token is a token given to the client from the server that @@ -215,7 +290,7 @@ your domain, you may be vulnerable to a mitm attack. ## Complete Example Handshake The following is a complete and reproducible handshake. Generated by the current -implementation of this spec in go-libp2p. +implementation of this spec in go-libp2p. This is a server-initiated handshake. Understanding the opaque value is not necessary in order to understand this spec. Servers are free to do whatever they want with the opaque field. The From ad8cd056d385a1495aceaa3cd69b18eea46ba8c8 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Thu, 5 Sep 2024 15:30:12 -0700 Subject: [PATCH 21/23] Add maximum header size suggestion --- http/peer-id-auth.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 165478228..3e651311d 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -275,6 +275,8 @@ id for the authentication endpoint is `/http-peer-id-auth/1.0.0`. * Implementations MUST only authenticate over a secured connection (i.e. TLS). * Implementations SHOULD limit the maximum length of any variable length field. + * The suggested Maximum length of the Authentication related header should is + 2048 bytes. ## Security Considerations From b51a4cc380bf153ad29a9c96f96c493ae43409b2 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 9 Sep 2024 17:00:00 -0700 Subject: [PATCH 22/23] Clarify that server may ignore client initiated handshake and start server initiated one --- http/peer-id-auth.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 3e651311d..64568b064 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -187,7 +187,10 @@ protocol operates as follows: The client initiated version of this handshake follows the same structure, except that the client sends initially sends a `challenge-server` and the order -of who is authenticated first is reversed. The protocol is as follows +of who is authenticated first is reversed. The server MAY ignore the initial +request, and respond by starting the Server initiated handshake. + +The client initiated handshake is as follows 1. The client makes an HTTP request to a known authenticated resource and sets the header: @@ -198,7 +201,7 @@ of who is authenticated first is reversed. The protocol is as follows 2. The server responds with status code 401 (Unauthorized) and set the header: ``` - WWW-Authenticate: libp2p-PeerID challenge-client="", opaque="", public-key="", sig="" + WWW-Authenticate: libp2p-PeerID challenge-client="", public-key="", sig="", opaque="" ``` The `sig` param represents a signature over the parameters: From e0740155d2e0561603e66a9270af1a9f9b3ed6c1 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Tue, 10 Sep 2024 16:13:03 -0700 Subject: [PATCH 23/23] Add client initiated example --- http/peer-id-auth.md | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/http/peer-id-auth.md b/http/peer-id-auth.md index 64568b064..ae575896b 100644 --- a/http/peer-id-auth.md +++ b/http/peer-id-auth.md @@ -292,7 +292,7 @@ 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. -## Complete Example Handshake +## Complete Server Initiated Handshake Example The following is a complete and reproducible handshake. Generated by the current implementation of this spec in go-libp2p. This is a server-initiated handshake. @@ -327,6 +327,34 @@ Note over Client: Future requests use the bearer token Client->>Server: Authorization=libp2p-PeerID bearer="YhlYjHWTMOkTleROtjMiChL7Mx15_GDYfi971mdJCqB7ImlzLXRva2VuIjp0cnVlLCJwZWVyLWlkIjoiMTJEM0tvb1dKV29hcVpoRGFvRUZzaEY3UmgxYnBZOW9oaWhGaHpjVzZkNjlMcjJOQVN1cSIsImhvc3RuYW1lIjoiZXhhbXBsZS5jb20iLCJjcmVhdGVkLXRpbWUiOiIxOTY5LTEyLTMxVDE2OjAwOjAwLTA4OjAwIn0=" ``` +## Complete Client Initiated Handshake Example + +Below is the same as above, but using the client initated handshake. + +### Parameters +| Parameter | Value | +| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| hostname | example.com | +| Server Private Key (pb encoded as hex) | 0801124001010101010101010101010101010101010101010101010101010101010101018a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c | +| Server HMAC Key (hex) | 0000000000000000000000000000000000000000000000000000000000000000 | +| Challenge Client | ERERERERERERERERERERERERERERERERERERERERERE= | +| Client Private Key (pb encoded as hex) | 0801124002020202020202020202020202020202020202020202020202020202020202028139770ea87d175f56a35466c34c7ecccb8d8a91b4ee37a25df60f5b8fc9b394 | +| Challenge Server | MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMz | +| "Now" time | 1970-01-01 00:00:00 +0000 UTC | + +### Handshake Diagram +```mermaid +sequenceDiagram +Client->>Server: Authorization=libp2p-PeerID challenge-server="MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMz", public-key="CAESIIE5dw6ofRdfVqNUZsNMfszLjYqRtO43ol32D1uPybOU" +Server->>Client: WWW-Authenticate=libp2p-PeerID challenge-client="ERERERERERERERERERERERERERERERERERERERERERE=", public-key="CAESIIqI4910CfGV_VLbLTy6XXLKZwm_HZQSG_N0iAG0D29c", sig="HQ7BJRaSpRhNCORNiALNJENdwXUyq0eM2cxNoxe-XnQw6oEAMaeYnjMYaHHjgq0XNxZmy4W2ngKUcI1CgprLCQ==", opaque="1JrloFj6hobNG859qexB0_odSQlwsb1QSFUMebPJLFp7ImNsaWVudC1wdWJsaWMta2V5IjoiQ0FFU0lJRTVkdzZvZlJkZlZxTlVac05NZnN6TGpZcVJ0TzQzb2wzMkQxdVB5Yk9VIiwiY2hhbGxlbmdlLWNsaWVudCI6IkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFUkU9IiwiaG9zdG5hbWUiOiJleGFtcGxlLmNvbSIsImNyZWF0ZWQtdGltZSI6IjE5NjktMTItMzFUMTY6MDA6MDAtMDg6MDAifQ==" +Note right of Client: Client has authenticated Server + +Client->>Server: Authorization=libp2p-PeerID opaque="1JrloFj6hobNG859qexB0_odSQlwsb1QSFUMebPJLFp7ImNsaWVudC1wdWJsaWMta2V5IjoiQ0FFU0lJRTVkdzZvZlJkZlZxTlVac05NZnN6TGpZcVJ0TzQzb2wzMkQxdVB5Yk9VIiwiY2hhbGxlbmdlLWNsaWVudCI6IkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFUkVSRVJFUkU9IiwiaG9zdG5hbWUiOiJleGFtcGxlLmNvbSIsImNyZWF0ZWQtdGltZSI6IjE5NjktMTItMzFUMTY6MDA6MDAtMDg6MDAifQ==", sig="OrwJPO4buHKJdKXP2av8PFwv3XF_-m5MqndskeVV5UzufYzBCTm7RBaFnBS1sEhuQHZSZPh9RJgN5NmLzrUrBQ==" +Note left of Server: Server has authenticated Client +Server->>Client: Authentication-Info=libp2p-PeerID bearer="YhlYjHWTMOkTleROtjMiChL7Mx15_GDYfi971mdJCqB7ImlzLXRva2VuIjp0cnVlLCJwZWVyLWlkIjoiMTJEM0tvb1dKV29hcVpoRGFvRUZzaEY3UmgxYnBZOW9oaWhGaHpjVzZkNjlMcjJOQVN1cSIsImhvc3RuYW1lIjoiZXhhbXBsZS5jb20iLCJjcmVhdGVkLXRpbWUiOiIxOTY5LTEyLTMxVDE2OjAwOjAwLTA4OjAwIn0=" +Note over Client: Future requests use the bearer token +``` + [Peer ID spec]: https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md [@MarcoPolo]: https://github.com/MarcoPolo