@@ -729,7 +729,7 @@ contain the following information about its OCM API:
729729 for a short-lived bearer token.
730730 _ `"http-sig"` - to indicate that this OCM Server supports
731731 [RFC9421] HTTP Message Signatures and advertises public keys in the
732- format specified by [RFC7515 ] at the `/.well-known/jwks.json`
732+ format specified by [RFC7517 ] at the `/.well-known/jwks.json`
733733 endpoint for signature verification.
734734 _ `"invites"` - to indicate the server would support acting as an
735735 Invite Sender or Invite Receiver OCM Server. This might be useful
@@ -1302,7 +1302,14 @@ https://datatracker.ietf.org/html/rfc6749)", October 2012.
13021302(URIs)](https://datatracker.ietf.org/doc/html/rfc8615)", May 2019
13031303
13041304[RFC7515] Jones, M., Bradley, J., Sakimura, N., "[JSON Web Signature
1305- (JWS)](https://datatracker.ietf.org/doc/html/rfc7515), May 2015."
1305+ (JWS)](https://datatracker.ietf.org/doc/html/rfc7515)", May 2015.
1306+
1307+ [RFC7517] Jones, M., "[JSON Web Key (JWK)](
1308+ https://datatracker.ietf.org/doc/html/rfc7517)", May 2015.
1309+
1310+ [RFC8032] Josefsson, S., Liusvaara, I., "[Edwards-Curve Digital
1311+ Signature Algorithm (EdDSA)](
1312+ https://datatracker.ietf.org/doc/html/rfc8032)", January 2017.
13061313
13071314
13081315# Appendix A: Multi-factor Authentication
@@ -1328,126 +1335,95 @@ out of scope for this specification: a mechanism similar to the
13281335[ScienceMesh](https://sciencemesh.io) integration for the
13291336[Invite](#invite-flow) capability may be envisaged.
13301337
1331- # Appendix B: Request Signing
1332-
1333- A request is signed by adding the signature in the headers. The sender
1334- also needs to expose the public key used to generate the signature. The
1335- receiver can then validate the signature and therefore the origin of
1336- the request.
1337- To help debugging, it is RECOMMENDED to also add all properties used in
1338- the signature as headers, even if they can easily be re-generated from
1339- the payload.
1338+ # Appendix B: JWKS and HTTP Signature Examples
13401339
1341- Note : Signed requests prove the identity of the sender but do not
1342- encrypt nor affect its payload.
1340+ # # JWKS Endpoint
13431341
1344- Here is an example of headers needed to sign a request.
1342+ An OCM Server that advertises the `http-sig` capability MUST expose its
1343+ public keys at `/.well-known/jwks.json` in the format specified by
1344+ [RFC7517]. Here is an example response from
1345+ `https://sender.example.org/.well-known/jwks.json` :
13451346
1347+ ` ` ` json
1348+ {
1349+ "keys": [
1350+ {
1351+ "kty": "OKP",
1352+ "crv": "Ed25519",
1353+ "kid": "sender.example.org#key1",
1354+ "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"
1355+ }
1356+ ]
1357+ }
13461358` ` `
1347- {
1348- "@request-target": "post /path",
1349- "content-length": 380,
1350- "date": "Mon, 08 Jul 2024 14:16:20 GMT",
1351- "content-digest": "SHA-256=U7gNVUQiixe5BRbp4...",
1352- "host": "hostname.of.the.recipient",
1353- "Signature": "keyId=\" https://author.hostname/key\" ,algorithm=
1354- \" rsa-sha256\" ,headers=\" content-length date digest host\" ,
1355- signature=\" DzN12OCS1rsA[...]o0VmxjQooRo6HHabg==\" "
1356- }
1357- ` ` `
13581359
1359- * '@request-target' (optional) contains the reached endpoint and
1360- the used method,
1361- * 'content-length' is the total length of the payload of the
1362- request,
1363- * 'date' is the date and time when the request has been
1364- sent,
1365- * 'content-digest' is a checksum of the payload of the
1366- request,
1367- * 'host' is the hostname of the recipient of the request (remote when
1368- signing outgoing request, local on incoming request),
1369- * 'Signature' contains the signature generated using the private key
1370- and details on its generation :
1371- - ' keyId' is a unique id, formatted as an url; hostname is used to
1372- retrieve the public key via custom discovery
1373- - ' algorithm' specify the algorithm used to generate signature
1374- - ' headers' specify the properties used when generating the
1375- signature
1376- - ' signature' the signature of an array containing the properties
1377- listed in 'headers'. Some properties like content-length, date,
1378- content-digest, and host are mandatory to protect against
1379- authenticity override.
1380-
1381- # # How to generate the Signature for outgoing request
1382-
1383- After properties are set in the headers, the Signature is generated and
1384- added to the list.
1385-
1386- This is a pseudo-code example for generating the `Signature` header for
1387- outgoing requests :
1360+ # # Signing a Request (Sender)
1361+
1362+ Given a Share Creation Notification request :
13881363
13891364` ` `
1390- headers = {
1391- 'content-length': length_of(payload),
1392- # Use a function to get the current GMT date as 'D, d M Y H:i:s T'
1393- 'date': current_gmt_datetime(),
1394- 'content-digest': 'SHA-256=' + base64_encode(hash('sha256',
1395- utf8_encode(payload))),
1396- 'host': 'recipient-fqdn',
1397- }
1365+ POST /ocm/shares HTTP/1.1
1366+ Host: receiver.example.org
1367+ Date: Tue, 20 Apr 2021 02:07:55 GMT
1368+ Content-Type: application/json
1369+ Content-Digest: sha-256=:LkpHyFOVbBDPxc7YbHDOWNzAv88qWuVfLNf4TUf9Uo8=:
13981370
1399- signed = ssl_sign(concatenate_with_newlines(headers),
1400- private_key, 'sha256')
1401- signature = {
1402- 'keyId': 'sender.fqdn', # The sending server's FQDN
1403- 'algorithm': 'rsa-sha256',
1404- 'headers': 'content-length date content-digest host',
1405- 'signature': signed,
1371+ {
1372+ 1373+ "name": "spec.yaml",
1374+ "providerId": "7c084226-d9a1-11e6-bf26-cec0c932ce01",
1375+ 1376+ 1377+ "ownerDisplayName": "Albert Einstein",
1378+ "senderDisplayName": "Albert Einstein",
1379+ "shareType": "user",
1380+ "resourceType": "file",
1381+ "protocol": {
1382+ "name": "multi",
1383+ "webdav": {
1384+ "uri": "spec.yaml",
1385+ "sharedSecret": "hfiuhworzwnur98d3wjiwhr",
1386+ "permissions": ["read", "write"]
1387+ }
1388+ }
14061389}
1407-
1408- headers['Signature'] = format_signature(signature)
14091390` ` `
14101391
1411- # # How to confirm Signature on incoming request
1412-
1413- The first step would be to confirm the validity of each
1414- properties :
1392+ The signature base is constructed according to [RFC9421] :
14151393
1416- * `content-length` and `content-digest` can be regenerated and compared
1417- from the payload of the request,
1418- * a maximum TTL MUST be applied to `date` and current
1419- timestamp,
1420- * regarding data contained in the `Signature`
1421- header :
1422- - using `keyId` to get the public key from remote
1423- signatory,
1424- - ` headers` is used to generate the clear version of the
1425- signature and MUST contain at least `content-length`, `date`,
1426- ` content-digest` and `host`,
1427- - ` signature` is the encrypted version of the
1428- signature.
1394+ ` ` `
1395+ "@method": POST
1396+ "@target-uri": https://receiver.example.org/ocm/shares
1397+ "content-digest": sha-256=:LkpHyFOVbBDPxc7YbHDOWNzAv88qWuVfLNf4TUf9Uo8=:
1398+ "@signature-params": ("@method" "@target-uri" "content-digest")\
1399+ ;created=1618884475;keyid="sender.example.org#key1";alg="ed25519"
1400+ ` ` `
14291401
1430- Here is an example of how to verify the signature using the headers,
1431- the signature and the public key :
1402+ Sign this base using, for example Ed25519 ([RFC8032]) to produce the
1403+ signature, then add headers :
14321404
14331405` ` `
1434- clear = {
1435- 'content-length': length_of(payload),
1436- 'date': 'Mon, 08 Jul 2024 14:16:20 GMT',
1437- 'content-digest': 'SHA-256=' + base64_encode(hash('sha256',
1438- utf8_encode(payload))), # Recompute digest for verification
1439- 'host': 'sender-fqdn',
1440- }
1406+ Signature-Input: sig1=("@method" "@target-uri" "content-digest")\
1407+ ;created=1618884475;keyid="sender.example.org#key1";alg="ed25519"
1408+ Signature: sig1=:dGVzdCBzaWduYXR1cmUgdmFsdWU=:
1409+ ` ` `
14411410
1442- signed = headers['Signature']
1443- verification_result = ssl_verify(concatenate_with_newlines(clear),
1444- signed, public_key, 'sha256')
1411+ # # Verifying a Signature (Receiver)
14451412
1446- if not verification_result then
1447- raise InvalidSignatureException
1448- ` ` `
1413+ To verify an incoming signed request :
14491414
1450- # # Validating the payload
1415+ 1. Extract the provider domain from the `sender` field in the
1416+ request body
1417+ 2. Fetch the public key from
1418+ ` https://<provider-domain>/.well-known/jwks.json`
1419+ 3. Extract `keyid` from `Signature-Input` header and find the key
1420+ matching the `kid` value in the [RFC7517] response
1421+ 4. Reconstruct the signature base from the request using the
1422+ components listed in `Signature-Input` as specified in [RFC9421]
1423+ 5. Verify the signature using the appropriate algorithm
1424+ (e.g., Ed25519 [RFC8032])
1425+
1426+ # # Validating the Payload
14511427
14521428Following the validation of the signature, the host SHOULD also confirm
14531429the validity of the payload, that is ensuring that the actions implied
@@ -1836,7 +1812,6 @@ to model a few key properties.
18361812* __type__: Type of Resource (file, folder, calendar, etc.)
18371813
18381814
1839-
18401815# Acknowledgements
18411816
18421817Our deepest thanks and appreciation go to the people who started the
0 commit comments