beckn / beckn/protocol-specifications-v2

CounterSignature spec — circular dependency when digest covers the Ack body that contains the signature

Open
#141 14 comments 0 reactions 1 assignee Claimed by @ravi-prakash-v View on GitHub
bug NFH-004
Dominant language
No language data
Stars
15
Forks
13
PR merge metrics
No merged PRs in 30d

Description

We are implementing `CounterSignature` support in the ONIX adapter as part of v2.0.0 LTS and have paused on this to seek clarification before shipping.

The spec defines the `CounterSignature` signing string as:

```
(created): {unixTimestamp}
(expires): {unixTimestamp}
digest: BLAKE-512={base64DigestOfAckBody}
(request-digest): BLAKE-512={base64DigestOfInboundRequestBody}
(message-id): {messageId}
```

And the `AckSchema` places the `CounterSignature` inside the Ack response body at `message.ack.signature`.

**The problem — circular dependency:**

The signing string requires `digest` = BLAKE-512 of the Ack body. But the signature itself is embedded inside that same Ack body. This creates a self-referential loop:

> To compute the signature → you need the digest of the Ack body
> To know the Ack body → you need the signature already placed inside it

This cannot be resolved without an unstated convention — for example, computing the digest over the Ack body with the `signature` field absent or empty, then inserting the computed value. The spec does not describe this process, which means two independent implementations could do this differently and fail to interoperate.

**What we believe the intent is:**

The primary semantic purpose of `CounterSignature` is to prove that the receiver received, authenticated, and processed the specific inbound request. That proof is carried by `(request-digest)` — the BLAKE-512 of the inbound request body. This field has no circularity problem because the inbound request body is fixed and available before the Ack is constructed.

If `(request-digest)` is the main field, the `digest` covering the Ack body appears to be an additional binding — but one that introduces the circular problem described above.

**Questions:**

1. **Is the `digest` field (covering the Ack body) intentional, or is `(request-digest)` the primary signed field?** If the intent is only to prove receipt of the inbound request, removing `digest` from the signing string would eliminate the circularity entirely.

2. **If `digest` over the Ack body is intentional, how should the signer construct the Ack body before computing that digest?** Specifically: should the `signature` field be absent, empty, or set to a placeholder when the digest is computed? The verifier needs to apply the exact same transformation to recompute and verify.

3. **Why is the signature placed in the Ack response body rather than in a response header?** Placing it in a header (analogous to how the request `Authorization` header carries the regular signature) would eliminate the circularity entirely, since the header is not part of the body being digested.

We want to implement this correctly and interoperably. A clarification on the intended construction and verification algorithm would unblock our implementation.

---

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.