-
Notifications
You must be signed in to change notification settings - Fork 17
Signing request #92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Signing request #92
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
341e943
signing request
ArtificialOwl 8412be2
fix PRIVATE/PUBLIC in example
michielbdejong a095ddc
remove empty newline
michielbdejong 635f7b6
rewording, switch from SHOULD to MAY
michielbdejong 2e1a022
typo
michielbdejong fd77284
mandatory -> recommended
michielbdejong 3166387
rewording
michielbdejong 35df4ad
rewording
michielbdejong 050ab2e
ras-sha256 -> rsa-sha256
michielbdejong 50e852b
Merge branch 'develop' into develop
michielbdejong 64602b8
typo
michielbdejong File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ the [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (fka Swagger) specif | |
| * [Share Updating](#update) | ||
| * [Resharing](#reshare) | ||
| * [Invite](#invite) | ||
| * [Signing Request](#signing-request) | ||
|
|
||
| * [Contributing](#contributing) | ||
|
|
||
|
|
@@ -35,6 +36,8 @@ If a finite whitelist of receiver servers exists on the sender side, then this l | |
|
|
||
| When a sending server allows sharing to any internet-hosted receiving server, then discovery can happen from the sharee address, using the `/.well-known/ocm` (or `/ocm-provider`, for backwards compatibility) URL that receiving servers SHOULD provide according to this [specification](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1.well-known~1ocm/get). | ||
|
|
||
| To ease the process of confirming the identity of a remote party, the discovery data MAY contain a public key: each incoming request that requires to origin from an authenticated source MUST be signed in its headers using the private key of that source, whose public key MUST be exposed in its discovery data. | ||
|
|
||
| To fill the gap between users knowning other peers' email addresses of the form `[email protected]`, and the actual cloud storage endpoints being in the form `https://my-cloud-storage.provider.org`, a further discovery mechanism MAY be provided in case hosting https://provider.org/.well-known/ocm is impractical, based on DNS `SRV` Service Records. | ||
|
|
||
| * If e.g. https://provider.org/.well-known/ocm does not exist, a provider MAY instead point to e.g. https://my-cloud-storage.provider.org/.well-known/ocm by ensuring that a `type=SRV` DNS query to `_ocm._tcp.provider.org` resolves to e.g. `service = 10 10 443 my-cloud-storage.provider.org` | ||
|
|
@@ -44,7 +47,7 @@ To fill the gap between users knowning other peers' email addresses of the form | |
| To create a share, the sending server SHOULD make a HTTP POST request to the `/shares` endpoint of the receiving server ([docs](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1shares/post)). | ||
|
|
||
| ### Share Acceptance | ||
| In response to a share creation, the receiving server MAY send back a [notification](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1notifications/post) to the sending server, with `notificationType` set to `"SHARE_ACCEPTED"` or `"SHARE_DECLINED"`. The sending server MAY expose this information to the end user. | ||
| In response to a share creation, the receiving server MAY send back a [notification](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1notifications/post) to the sending server, with `notificationType` set to `"SHARE_ACCEPTED"` or `"SHARE_DECLINED"`. The sending server MAY expose this information to the end user. | ||
|
|
||
| ### Share Access | ||
| To access a share, the receiving server MAY use multiple ways, depending on the received payload and on the `protocol.name` property: | ||
|
|
@@ -90,6 +93,102 @@ If an OCM provider exposes the capability `/mfa-capable`, it indicates that it w | |
| Since there is no way to guarantee that the sharee OCM provider will actually enforce the MFA requirement, it is up to the sharer OCM provider to establish a trust with the OCM sharee provider such that it is reasonable to assume that the sharee OCM provider will honor the MFA requirement. This establishment of trust will inevitably be implementation dependent, and can be done for example using a pre approved allow list of trusted OCM providers. The procedure of establishing trust is out of scope for this specification: a mechanism similar to the [ScienceMesh](https://sciencemesh.io) integration for the [Invite](#invite) capability may be envisaged. | ||
|
|
||
|
|
||
| ## Signing request | ||
|
|
||
| A request is signed by adding the signature in the headers. The sender also needs to expose the public key used to generate the signature. The receiver can then validate the signature and therefore the origin of the request. | ||
| To help debugging, it is recommended to also add all properties used in the signature as headers, even if they can easily be re-generated from the payload. | ||
|
|
||
| Note: Signed requests prove the identity of the sender but does not encrypt nor affect its payload. | ||
|
|
||
| Here is an example of headers needed to sign a request. | ||
|
|
||
| ``` | ||
| { | ||
| "(request-target)": "post /path", | ||
| "content-length": 380, | ||
| "date": "Mon, 08 Jul 2024 14:16:20 GMT", | ||
| "digest": "SHA-256=U7gNVUQiixe5BRbp4Tg0xCZMTcSWXXUZI2\\/xtHM40S0=", | ||
| "host": "hostname.of.the.recipient", | ||
| "Signature": "keyId=\"https://author.hostname/key\",algorithm=\"rsa-sha256\",headers=\"content-length date digest host\",signature=\"DzN12OCS1rsA[...]o0VmxjQooRo6HHabg==\"" | ||
| } | ||
| ``` | ||
|
|
||
| - '(request-target)' contains the reached endpoint and the used method, | ||
| - 'content-length' is the total length of the payload of the request, | ||
| - 'date' is the date and time when the request has been sent, | ||
| - 'digest' is a checksum of the payload of the request, | ||
| - 'host' is the hostname of the recipient of the request (remote when signing outgoing request, local on incoming request), | ||
| - 'Signature' contains the signature generated using the private key and details on its generation: | ||
| * 'keyId' is a unique id, formatted as an url. hostname is used to retrieve the public key via custom discovery | ||
| * 'algorithm' specify the algorithm used to generate signature | ||
| * 'headers' specify the properties used when generating the signature | ||
| * 'signature' the signature of an array containing the properties listed in 'headers'. Some properties like content-length, date, digest, and host are mandatory to protect against authenticity override. | ||
|
|
||
|
|
||
| ### How to generate the Signature for outgoing request | ||
|
|
||
| After properties are set in the headers, the Signature is generated and added to the list. | ||
|
|
||
| This is a quick PHP example of headers for outgoing request: | ||
|
|
||
| ```php | ||
| $headers = [ | ||
| '(request-target)' => 'post /path', | ||
| 'content-length' => strlen($payload), | ||
| 'date' => gmdate('D, d M Y H:i:s T'), | ||
| 'digest': 'SHA-256=' . base64_encode(hash('sha256', utf8_encode($payload), true)), | ||
| 'host': 'hostname.of.the.recipient', | ||
| ]; | ||
|
|
||
| openssl_sign(implode("\n", $headers), $signed, $privateKey, OPENSSL_ALGO_SHA256); | ||
|
|
||
| $signature = [ | ||
| 'keyId' => 'https://author.hostname/key', | ||
| 'algorithm' => 'rsa-sha256', | ||
| 'headers' => 'content-length date digest host', | ||
| 'signature' => $signed | ||
| ]; | ||
|
|
||
| $headers['Signature'] = implode(',', $signature); | ||
| ``` | ||
|
|
||
|
|
||
| ### How to confirm Signature on incoming request | ||
|
|
||
| The first step would be to confirm the validity of each properties: | ||
|
|
||
| - '(request-target)' and 'host' are immutable to the type of the request and the local/current host, | ||
| - 'content-length' and 'digest' can be re-generated and compared from the payload of the request, | ||
| - A maximum TTL must be applied to 'date' and current timestamp, | ||
| - regarding data contained in the 'Signature' header: | ||
| * using 'keyId' to get the public key from remote signatory, | ||
| * 'headers' is used to generate the clear version of the signature and must contain at least 'content-length', 'date', 'digest' and 'host', | ||
| * 'signature' is the encrypted version of the signature. | ||
|
|
||
| Here is an example of how to verify the signature using the headers, the signature and the public key: | ||
|
|
||
| ```php | ||
michielbdejong marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| $clear = [ | ||
| '(request-target)' => 'post /path', | ||
| 'content-length' => strlen($payload), | ||
| 'date' => 'Mon, 08 Jul 2024 14:16:20 GMT', | ||
| 'digest': 'SHA-256=' . base64_encode(hash('sha256', utf8_encode($payload), true)), | ||
| 'host': $localhost | ||
| ]; | ||
|
|
||
| $signed = "DzN12OCS1rsA[...]o0VmxjQooRo6HHabg=="; | ||
| if (openssl_verify(implode("\n", $clear), $signed, $publicKey, 'sha256') !== 1) { | ||
| throw new InvalidSignatureException('signature issue'); | ||
| } | ||
| ``` | ||
|
|
||
| ### Validating the payload | ||
|
|
||
| Following the validation of the signature, the host should also confirm the validity of the payload, that is ensuring that the actions implied in the payload actually initiated on behalf of the source of the request. | ||
|
|
||
| As an example, if the payload is about initiating a new share the file owner has to be an account from the instance at the origin of the request. | ||
michielbdejong marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| ## Changelog | ||
|
|
||
| [Available here](CHANGELOG.md) | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.