diff --git a/README.md b/README.md index ac89052..fd5ed3e 100644 --- a/README.md +++ b/README.md @@ -491,6 +491,7 @@ Only after receiving the initial `server/activate` should the client send any ot - `pairing?`: object - parameters of the pairing attempt this activation admits. Required when `'pairing'` is in `activities`; absent otherwise. A client ignores this field when `activities` does not include `'pairing'`. - `method`: 'dynamic_pin' | 'pairing_psk' | 'static_pin' - pairing method the server picked, drawn from the client's `supported_pair_methods`. - `pin_length?`: integer - the dynamic [PIN length](#dynamic-pin-pairing-flow) for this session. Required when `method` is `'dynamic_pin'`; absent otherwise. + - `languages?`: string[] - non-empty list of [BCP 47](https://www.rfc-editor.org/rfc/rfc5646) language tags in descending operator preference (e.g. `["ca", "es", "en"]`), for spoken [PIN emission](#dynamic-pin-pairing-flow). Optional when `method` is `'dynamic_pin'`; absent otherwise. The activity sets the server may legitimately declare are constrained by which PSK matched during the [Noise handshake](#encryption): @@ -885,6 +886,8 @@ PIN = decimal(PIN_int) zero-padded to L digits The hash input is the UTF-8 bytes of the literal label `"sendspin-pin-derive-v1"` (no separator, no NUL terminator) followed by `h` (32 bytes, raw), `nonce_A` (32 bytes, raw), and `nonce_B` (32 bytes, raw). The full 32-byte SHA-256 output is interpreted as an unsigned big-endian 256-bit integer; the PIN is its value modulo 10^L, zero-padded on the left to exactly `L` ASCII digits. The PIN bytes fed into CPace as `PRS` are these `L` ASCII digits - the same per-digit encoding as the static PIN. +**PIN emission.** When emitting the PIN through a spoken channel, the client SHOULD use the best-matching language it supports, treating the activation's [`languages`](#server--client-serveractivate) as the language priority list under [RFC 4647](https://www.rfc-editor.org/rfc/rfc4647#section-3.4) Lookup matching, and falling back to its own default when nothing matches. The hint is informational and never grounds for [`pair/abort`](#client--server-pairabort); display emission is unaffected. + **Client verification.** On receipt of [`server/pair-confirm`](#server--client-serverpair-confirm), the client verifies the CPace MCF tag `server_kc`. On failure the client sends [`pair/abort`](#client--server-pairabort) with reason `pin_mismatch`. **Server verification.** When [`client/pair-confirm`](#client--server-clientpair-confirm) arrives, the server verifies, in this order: diff --git a/messaging.md b/messaging.md index 7cd09b7..7d419ae 100644 --- a/messaging.md +++ b/messaging.md @@ -210,6 +210,7 @@ Only after receiving the initial `server/activate` should the client send any ot - `pairing?`: object - parameters of the pairing attempt this activation admits. Required when `'pairing'` is in `activities`; absent otherwise. A client ignores this field when `activities` does not include `'pairing'`. - `method`: 'dynamic_pin' | 'pairing_psk' | 'static_pin' - pairing method the server picked, drawn from the client's `supported_pair_methods`. - `pin_length?`: integer - the dynamic [PIN length](pairing.md#dynamic-pin-pairing-flow) for this session. Required when `method` is `'dynamic_pin'`; absent otherwise. + - `languages?`: string[] - non-empty list of [BCP 47](https://www.rfc-editor.org/rfc/rfc5646) language tags in descending operator preference (e.g. `["ca", "es", "en"]`), for spoken [PIN emission](pairing.md#dynamic-pin-pairing-flow). Optional when `method` is `'dynamic_pin'`; absent otherwise. The activity sets the server may legitimately declare are constrained by which PSK matched during the [Noise handshake](connection.md#encryption): diff --git a/pairing.md b/pairing.md index f9aa2ed..c94875e 100644 --- a/pairing.md +++ b/pairing.md @@ -160,6 +160,8 @@ PIN = decimal(PIN_int) zero-padded to L digits The hash input is the UTF-8 bytes of the literal label `"sendspin-pin-derive-v1"` (no separator, no NUL terminator) followed by `h` (32 bytes, raw), `nonce_A` (32 bytes, raw), and `nonce_B` (32 bytes, raw). The full 32-byte SHA-256 output is interpreted as an unsigned big-endian 256-bit integer; the PIN is its value modulo 10^L, zero-padded on the left to exactly `L` ASCII digits. The PIN bytes fed into CPace as `PRS` are these `L` ASCII digits - the same per-digit encoding as the static PIN. +**PIN emission.** When emitting the PIN through a spoken channel, the client SHOULD use the best-matching language it supports, treating the activation's [`languages`](messaging.md#server--client-serveractivate) as the language priority list under [RFC 4647](https://www.rfc-editor.org/rfc/rfc4647#section-3.4) Lookup matching, and falling back to its own default when nothing matches. The hint is informational and never grounds for [`pair/abort`](#client--server-pairabort); display emission is unaffected. + **Client verification.** On receipt of [`server/pair-confirm`](#server--client-serverpair-confirm), the client verifies the CPace MCF tag `server_kc`. On failure the client sends [`pair/abort`](#client--server-pairabort) with reason `pin_mismatch`. **Server verification.** When [`client/pair-confirm`](#client--server-clientpair-confirm) arrives, the server verifies, in this order: