From a744bbe6be116530a58902f77f3f42dd32632370 Mon Sep 17 00:00:00 2001 From: nishthamongaondc <150439595+nishthamongaondc@users.noreply.github.com> Date: Tue, 31 Mar 2026 23:50:44 +0530 Subject: [PATCH 1/2] Update form_encryption_and_signing.md --- registry/form_encryption_and_signing.md | 79 ++++++++++++++++++++----- 1 file changed, 64 insertions(+), 15 deletions(-) diff --git a/registry/form_encryption_and_signing.md b/registry/form_encryption_and_signing.md index 829d71c..f190823 100644 --- a/registry/form_encryption_and_signing.md +++ b/registry/form_encryption_and_signing.md @@ -6,7 +6,6 @@ All form payloads submitted between Network Participants (NPs) are **fully encry - The entire form body is encrypted — no individual field is readable in transit. - A signature over the encrypted payload ensures authenticity and tamper detection. -- The signature hash can also be computed from `formData` text (for example, the output of `JSON.stringify(formData)`) if both NPs use the exact same format. - The receiver can decrypt and validate the form independently. --- @@ -18,7 +17,7 @@ All form payloads submitted between Network Participants (NPs) are **fully encry | X25519 / Diffie-Hellman | Shared secret derivation | | AES-256-GCM | Symmetric payload encryption | | Ed25519 | Payload signing & verification | -| SHA-256 | Hashing before signing | +| BLAKE-512 | Digest generation before signing | | Base64 | Binary-to-string encoding for transport | --- @@ -65,35 +64,78 @@ encrypted_payload = Base64.encode(iv + ciphertext + authTag) ### Step 3 · Sign Encrypted Payload +Signing follows the same method used for Beckn API request signing (see [ONDC Network Signing & Verifying](./signing-verification.md)). + +**3a. Generate the digest** of the `encrypted_payload` using BLAKE-512: + +``` +digest = Base64.encode( BLAKE512( encrypted_payload ) ) ``` -hash = SHA256(encrypted_payload) +**3b. Construct the signing string** using `created`, `expires`, and the digest: + +``` +signing_string = "(created): {created_unix_ts}\n(expires): {expires_unix_ts}\ndigest:BLAKE-512={digest}" +``` + +**3c. Sign the signing string** using the NP's Ed25519 signing private key: + +``` signature = Ed25519.sign( - data = hash, + data = signing_string, privateKey = NP1_sign_private_key ) encoded_signature = Base64.encode(signature) ``` -> Alternate hashing input (if agreed): `hash = SHA256(JSON.stringify(formData))`. -> In this mode, both sender and receiver must hash the exact same form-data text format. +**3d. Set the Authorization header:** + +``` +Authorization: Signature + keyId="{subscriber_id}|{unique_key_id}|ed25519", + algorithm="ed25519", + created="{created_unix_ts}", + expires="{expires_unix_ts}", + headers="(created) (expires) digest", + signature="{encoded_signature}" +``` + +> For a worked example of key generation, digest computation, and Authorization header construction, refer to [ONDC Network Signing & Verifying](./signing-verification.md#authorization-header). ### Step 4 · Submit Form +Include the full `context` as plaintext, the `encrypted_payload`, and the `Authorization` header carrying the signature: + +**Request Body:** + ```json { "context": { - "np_id": "buyer-app.ondc.org", + "domain": "ONDC:FIS13", + "country": "IND", + "city": "std:080", + "action": "confirm", + "core_version": "2.1.0", + "bap_id": "buyer-app.ondc.org", + "bap_uri": "https://buyer-app.ondc.org/protocol/v1", + "bpp_id": "seller-app.ondc.org", + "bpp_uri": "https://seller-app.ondc.org/protocol/v1", "transaction_id": "123e4567-e89b-12d3-a456-426614174000", + "message_id": "123e4567-e89b-12d3-a456-426614174001", "timestamp": "2024-03-23T18:25:43.511Z" }, - "encrypted_payload": "", - "signature": "" + "encrypted_payload": "" } ``` -> `context` is always plaintext. `encrypted_payload` and `signature` carry the secured form data. +**Authorization Header:** + +``` +Signature keyId="buyer-app.ondc.org|207|ed25519",algorithm="ed25519",created="1641287875",expires="1641291475",headers="(created) (expires) digest",signature="fKQWvXhln4UdyZdL87ViXQObdBme0dHnsclD2LvvnHoNxIgcvAwUZOmwAnH5QKi9Upg5tRaxpoGhCFGHD+d+Bw==" +``` + +> `context` is always plaintext. `encrypted_payload` carries the secured form data. The signature travels in the `Authorization` header, not in the request body. --- @@ -104,10 +146,18 @@ encoded_signature = Base64.encode(signature) ``` np1_sign_public_key = registry.fetchSigningPublicKey(np1_id) -hash = SHA256(encrypted_payload) +// Extract fields from the Authorization header +auth = parseAuthorizationHeader(request.headers["Authorization"]) +created = auth.created +expires = auth.expires +signature = auth.signature + +digest = Base64.encode( BLAKE512( encrypted_payload ) ) + +signing_string = "(created): {created}\n(expires): {expires}\ndigest:BLAKE-512={digest}" isValid = Ed25519.verify( - data = hash, + data = signing_string, signature = Base64.decode(signature), publicKey = np1_sign_public_key ) @@ -116,7 +166,6 @@ if NOT isValid → REJECT("Signature verification failed") ``` > Always verify signature **before** decrypting. -> If payload-hash signing is used, compute the hash from the same `JSON.stringify(formData)` output as the sender. ### Step 6 · Derive Shared Key @@ -167,8 +216,8 @@ NP1 (Sender) NP2 (Receiver) 3. JSON.stringify(formData) 4. Generate random IV (12 bytes) 5. AES_GCM.encrypt → Base64 encode -6. SHA256(encrypted_payload) -7. Ed25519.sign → Base64 encode +6. BLAKE512(encrypted_payload) → construct signing string +7. Ed25519.sign → Base64 encode → Authorization header 8. POST { context, encrypted_payload, signature } 9. Fetch NP1 sign public key (registry) 10. Ed25519.verify → REJECT if invalid From d0b837f1b80931c40ffed035156b9fb46b721637 Mon Sep 17 00:00:00 2001 From: nishthamongaondc <150439595+nishthamongaondc@users.noreply.github.com> Date: Wed, 8 Apr 2026 18:57:29 +0530 Subject: [PATCH 2/2] Update form_encryption_and_signing.md --- registry/form_encryption_and_signing.md | 27 +++++++++++++++---------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/registry/form_encryption_and_signing.md b/registry/form_encryption_and_signing.md index f190823..76db509 100644 --- a/registry/form_encryption_and_signing.md +++ b/registry/form_encryption_and_signing.md @@ -112,18 +112,23 @@ Include the full `context` as plaintext, the `encrypted_payload`, and the `Autho ```json { "context": { + "action": "search", + "bap_id": "fis.test.bap.io", + "bap_uri": "https://fis.test.bap.io/", "domain": "ONDC:FIS13", - "country": "IND", - "city": "std:080", - "action": "confirm", - "core_version": "2.1.0", - "bap_id": "buyer-app.ondc.org", - "bap_uri": "https://buyer-app.ondc.org/protocol/v1", - "bpp_id": "seller-app.ondc.org", - "bpp_uri": "https://seller-app.ondc.org/protocol/v1", - "transaction_id": "123e4567-e89b-12d3-a456-426614174000", - "message_id": "123e4567-e89b-12d3-a456-426614174001", - "timestamp": "2024-03-23T18:25:43.511Z" + "location": { + "country": { + "code": "IND" + }, + "city": { + "code": "*" + } + }, + "message_id": "385e3fb7-61e6-49a7-acbc-8ac05f934d4d", + "timestamp": "2023-07-24T05:39:31.700Z", + "transaction_id": "c04a04ee-d892-400f-bbe6-479a43b4448a", + "ttl": "PT24H", + "version": "2.0.1" }, "encrypted_payload": "" }