Skip to content

Add encryption support#84

Draft
arturpragacz wants to merge 21 commits into
mainfrom
ap/encryption
Draft

Add encryption support#84
arturpragacz wants to merge 21 commits into
mainfrom
ap/encryption

Conversation

@arturpragacz

Copy link
Copy Markdown

Add encryption support

Comment thread README.md
Comment thread README.md
Comment thread README.md
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md
Comment thread README.md Outdated
Comment thread README.md
Comment thread README.md
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md
Comment thread README.md Outdated
Comment thread README.md
Comment thread README.md Outdated
Comment thread README.md
Comment thread README.md Outdated
- **Per-method failure counter.** The client maintains a failure counter for each PIN-pairing method family (`static_pin` and `dynamic_pin` tracked independently). The counter is persisted across reboots. It is not partitioned by `server_id` or source IP: a single per-method counter for the device.
- **Increment.** The counter for a method increments on each inner-authentication failure observed in that method's flow.
- **Reset.** The counter for a method resets to zero on a successful pairing finalization for that method.
- **Terminal lockout.** When a method's counter reaches **10**, the method enters a **terminal lockout** state: the client refuses all pairing attempts for that method indefinitely. Exit requires a deliberate, local operator action (manufacturer-defined), or writing `locked_out: false` for the method via [`management/set-pairing-config`](#server--client-managementset-pairing-config) from an `owner`-trust server; on successful exit the counter resets to zero. The client SHOULD surface the lockout to the operator via a device-local mechanism (LED, on-screen indicator, audible cue). If a server initiates a pairing-mode connection during terminal lockout, the client sends [`pair/abort`](#client--server-pairabort) with reason `locked_out` and closes.

@maximmaxim345 maximmaxim345 May 28, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seams a bit dangerous if the client doesn't have an owner to reset the lockout (or the user doesn't know where to find this option).

Also not sure if this should be signaled by an LED or audible cue, especially if Sendspin isn't the only protocol implemented on the device. There will potentially be an always glowing red LED on the device until you do a factory reset.

Can we make this silent and reset on reboot? The default action is to restart hardware if something stops working.

I know that a local action must be provided by the client/device, but having to do a full reset is a pretty bad experience. And requiring manufacturers to create a Sendspin-only reset shortcut is also difficult.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I softened the indicator requirements here.

Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md
Comment thread README.md
Comment thread README.md

All Sendspin connections use end-to-end encryption based on the [Noise Protocol Framework](https://noiseprotocol.org/noise.html). Encryption is mandatory for all connections established through the standard discovery mechanisms described in [Establishing a Connection](#establishing-a-connection).

Specialized deployments where the connection is tunneled through a separately authenticated and encrypted channel may expose a non-standard endpoint that omits the Noise handshake; such endpoints must not be advertised via mDNS, and the operator is responsible for ensuring the tunnel provides equivalent security guarantees.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should drop this from the spec. If these kind of things are done, they are not spec compliant and it's up to the project to make sure it works.

Comment thread README.md

**Security properties.** Forward secrecy is provided by the ephemeral-key DH in each handshake: compromise of static keys or the PSK does not retroactively decrypt prior sessions. Replay protection is provided by Noise's per-direction transport counter; a repeated or out-of-order ciphertext fails AEAD decryption and aborts the connection.

### Cipher Suites

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some headers have been adjusted to lowercase the 2nd word, here we capitalize 2nd word.

Comment thread README.md Outdated
Comment thread README.md
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Server->>Client: server/hello (name)
Client->>Server: client/hello (supported_pair_methods)
Note over Server: Operator picks dynamic PIN
Server->>Client: server/activate (activities=['pairing'], selected_pair_method=dynamic_pin)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this server/activate and not server/pair ? And if we receive that, track that current client is in pairing mode.

One thing we see by re-using server/activate for multiple purposes, is that we are including fields for one of the purpopses (ie selected_pair_method)

Comment thread README.md Outdated
Comment thread README.md
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md
Comment thread README.md Outdated
Comment thread README.md
**Note:** Servers normally activate the client's [preferred](#priority-and-activation) version of each role, but MAY omit a role at their discretion (e.g., based on trust level, deployment context, or operator policy). Checking `active_roles` is therefore required to determine what the client may actually use on this session.

**Note:** Servers will always activate the client's [preferred](#priority-and-activation) version of each role. Checking `active_roles` is only necessary to detect outdated servers or confirm activation of [application-specific roles](#application-specific-roles).
**Note:** When a `server/activate` removes a role from `active_roles`, the server first sends [`stream/end`](#server--client-streamend) for any stream that role still has open, so the client never holds a live stream for an inactive role.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stream/end only applies to binary message roles. Should we specify roles that just send JSON; e.g., color and metadata, should clear all fields before removing those roles?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants