detect_hash — Group 1, Code 12

Prove knowledge of an authenticity number without transmitting it.

Status: PROPOSED — not yet implemented

This document specifies a new command for the RAIDA server team. It is written so the server and client implementations can proceed in parallel from one agreed wire format. Nothing in the deployed network implements Code 12 today.

Purpose

detect_hash answers exactly the same question as detect — "is this the correct authenticity number for this coin?" — but the client sends a one-way hash of the AN instead of the AN itself. The AN is never placed on the wire, and the AN is not changed by this command.

Why this command is needed

An ordinary detect request must be encrypted, and the encryption key is itself derived from a coin's ANs (a "helper coin"). That creates a circular dependency for encrypted wallets:

  • CloudCoin wallet files can be encrypted with a user password (Format 10).
  • Format 10 deliberately contains no password check of any kind — no MAC, no tag, no verifier — so that a stolen drive gives an attacker nothing to test candidate passwords against offline.
  • Consequently the only way to test a password is to decrypt a coin and ask the RAIDA whether its ANs are authentic.
  • But if every coin in the wallet is encrypted, a wrong password also garbles the helper coin, so the request is malformed and the server rejects it. The client learns "I could not ask you", not "the answer is no", and cannot tell the user their password is wrong.

Because a hash discloses nothing, detect_hash can be sent unencrypted. No helper coin is required, so the circular dependency disappears and a wrong password produces a real negative answer.

Rejected alternatives, and why

Sending the AN in cleartext: analysed and rejected. Ownership requires 13 of 25 ANs, so a few disclosures are not immediately fatal, but disclosure is permanent — there is no operation that revokes a leaked AN (fix_encryption reinstalls the same AN to repair an encryption-secret mismatch; it does not rotate). Any budget for "how many may we safely expose" would also have to be derived from local file state, which is attacker-writable.

Sending an arithmetic sum of the AN bytes: rejected. A 16-byte AN sums to one of only ~4,081 values, and collisions are trivial to construct (9+130+240 equals 240+130+9 equals 100+100+179). It would be forgeable in microseconds.

Reusing detect_sum (Code 11): rejected. Its aggregate is an XOR, which is reversible — an attacker who knows the other ANs recovers the unknown one by XOR-ing them back out.

Construction

For each coin the client computes:

proof = SHA-256( challenge || DN || SN || AN )

where:

  • challenge is the 16-byte challenge already present in the request body (same field and position as detect).
  • DN is the 1-byte denomination and SN the 4-byte big-endian serial number of the coin being tested.
  • AN is the 16-byte authenticity number the client believes is correct.
  • || denotes byte concatenation, in exactly this order, with no separators or padding.

The server holds the real AN. It performs the same computation using its stored value and compares the two 32-byte results. Equal means the client knows the AN; unequal means it does not.

Why each input is present

InputReason it must be included
challengeMakes each proof distinct. A fresh random challenge per request means the same AN never produces the same proof twice, so proofs cannot be correlated across requests or accumulated into a dictionary. The client generates it; see the stateless-server note below.
DN and SNBinds the proof to one coin. Prevents a proof captured for one coin being replayed against another, and prevents a malicious server from harvesting proofs and relaying them to a different RAIDA.
ANThe secret being proven.

The server keeps no state, and does not track challenges

The challenge is generated by the CLIENT, freshly at random for every request, exactly as it does today. The RAIDA does not record challenges, does not enforce single use, and stores nothing between requests. Design principle: keep work off the RAIDA servers.

Why replay does not matter here. An attacker who captures a request also captures its response, which travels in the clear. Replaying the captured pair returns the same answer they already saw. It reveals no AN (that would require reversing SHA-256), it cannot alter any coin (detect_hash is read-only), and it cannot be redirected to another coin or another RAIDA (DN and SN are inside the hash). The only marginal gain is learning that the AN is still valid now rather than when it was captured — negligible, and strictly less than what the same attacker gains from sniffing an ordinary detect request, which hands over the AN itself.

A client that reused a challenge would only weaken its own privacy, never another user's, and never the network's integrity. Fresh challenges are therefore in the client's own interest and need no server enforcement.

Request Body

Identical to detect except that the 16-byte AN field is replaced by a 32-byte proof. The coin entry therefore grows from 21 to 37 bytes.

OffsetSizeFieldDescription
016ChallengeClient-generated random value, fresh per request. Not tracked by the server.
161DNDenomination of the first coin.
174SNSerial number, big-endian.
2132ProofSHA-256(challenge || DN || SN || AN).
53…37 each(repeats)Additional coin entries, same layout.
2Terminator3E 3E.

Response Body

Identical to detect in every respect, so existing client parsing logic applies unchanged: status 241 for all pass, 242 for all fail, and 243 with a bitfield when results are mixed. See the overview.

Semantics and constraints

  • Read-only. detect_hash never modifies an AN, never rotates ownership, and never changes server state for the coin. It is the hash analogue of detect, not of pown.
  • Client encryption type: 0 (unencrypted). Clients MUST send detect_hash with encryption type 0. Sending it unencrypted is the entire point — it requires no helper coin. RAIDAX continues to process header encryption through its normal shared protocol layer; this command adds no encryption-type validation of its own. See the note below for why encrypted client variants are excluded rather than merely unspecified.
  • Shard byte is reserved. Clients may leave the shard byte at its current value. RAIDAX ignores it for this command and uses its normal coin store.
  • New command code. Code 12 is used rather than extending Code 10 so that a server which does not implement it rejects the request cleanly instead of misinterpreting a 37-byte entry as a 21-byte one. Clients detect non-support and fall back to detect.
  • Rate limiting. Because this command can be used to test candidate passwords, it is an online guessing oracle by nature. Servers SHOULD apply the same or stricter abuse controls as detect.
  • Hash algorithm. SHA-256, as already used elsewhere in the client. If the server team prefers a different function, any preimage-resistant hash with at least a 256-bit output is acceptable provided both sides agree; the choice should be fixed in this document rather than negotiated per request.

Why clients use encryption type 0 only (server team ruling, accepted)

An earlier draft of this page said the client "MAY also" send this command encrypted. That was wrong and has been removed. The RAIDAX server team pointed out that the challenge does not have one uniform meaning across encryption types:

  • Type 0 — the wallet-password use case. Clear and straightforward.
  • Types 1 and 2 — legacy encryption transforms the challenge into RAIDAX's derived challenge_hash. The proof definition would have to state whether the hash covers the raw decrypted 16 bytes or that derived value. Ambiguity here means every proof fails.
  • Types 4 and 5 — not presently shaped compatibly with cmd_detect's legacy challenge/payload layout.
  • Type 8 — preserves the 16-byte body challenge, but its response framing differs.

Because the client/server hash inputs must match byte for byte, an under-specified challenge is not a cosmetic gap — it silently breaks every request. Type 0 is therefore the whole initial client contract. Encrypted variants may be added later, but only alongside an exact per-type definition of what the challenge bytes are. The RAIDAX command handler does not add a special encryption rejection; packet encryption remains the shared protocol layer's responsibility.

Reserved shard byte

The shard byte is reserved for future use. The client currently writes shard = 0 unconditionally, and Format 10 has no shard field at all.

For detect_hash, RAIDAX deliberately ignores this byte and uses its normal coin store. No shard-specific backend or rejection behavior is part of this command.

Security properties

PropertyAssessment
Eavesdropper learns the ANNo. Recovering a 16-byte AN from SHA-256 requires ~2128 work. Unlike a sum or XOR there is no algebraic shortcut.
Eavesdropper replays a captured proofPossible, and harmless. The reply is the same one they already captured in the clear. No AN is revealed, no coin is modified, and the proof cannot be aimed at a different coin or RAIDA. The only gain is confirming the AN is still valid now — far less than sniffing an ordinary detect, which discloses the AN outright.
Proof reused against another coin or another RAIDANo. DN and SN are inside the hash.
Attacker forges a proof without the ANNo. Requires guessing a 128-bit secret.
Offline password testing from a stolen fileUnchanged. This command adds no password-dependent value to any file. The Format 10 non-confirmability property is preserved.
Online password testingPossible by design, and mitigated only by server rate limiting — the same exposure the existing detect path already has.

Compared with the alternative of transmitting ANs in the clear, this command discloses nothing, requires no exposure budget, and requires no subsequent AN rotation to clean up. That is why it is preferred.

Client use

The CloudCoin client will use detect_hash to verify a candidate wallet password:

  1. Derive the key from the user's password and decrypt one coin.
  2. Send detect_hash to the RAIDA for that coin's ANs.
  3. 13 or more authentic responses confirm the password. A quorum of negative responses means the password is wrong (or the coin is genuinely counterfeit — the client cannot distinguish these and must not claim to).
  4. Too few usable responses is inconclusive: the wallet stays read-only and the user is told to retry when the network is available.

See How File Encryption Works for the client-side model.

Open questions for the server team

Settled: challenge handling (client-generated, fresh per request, never tracked — the RAIDA stores and enforces nothing); client encryption type 0; and reserved shard handling.

  1. Command code. Code 12 is free in Group 1.
  2. Hash choice. SHA-256 is the agreed function.
  3. Batch limits. With RAIDAX's 65,536-byte body limit, the 37-byte entry format supports up to 1,770 coins per request.

The server implementation is then: read the coin entry, compute SHA-256(challenge || DN || SN || stored_AN), compare with the supplied 32 bytes, and answer exactly as detect does. No new storage, no new lifecycle, no per-client state.