How commands are signed
A computer running Tealhand Host does only what a command signed by a device on your owner-signed device list asks: your phone with its hardware key, or one of your other computers. This page describes the keys, the command, the checks and the signed device list (the roster), as the code does them.
The keys
Section titled “The keys”| Key | Kind | Where | Signs |
|---|---|---|---|
| Phone iroh key | Ed25519 | Keystore-encrypted storage on the phone | Nothing: it proves the phone’s network identity in the QUIC handshake |
Phone device key |
P-256 | Android Keystore, cannot be exported | Every command, every device list, and the connection proof. No prompt |
Phone presence key |
P-256 | Android Keystore, signs only after a fingerprint or PIN on each use | The commands that need you there, and the connection proof |
| Computer iroh key | Ed25519 | The OS credential store, or a 0600 file on Linux without one |
It proves the computer’s network identity, and signs that computer’s commands to your other computers |
Two phone keys are needed because the computer can only know “the person pressed their finger” if the key
itself refuses to sign without it. The device key is also the owner key of your device list.
Text forms: a device’s iroh key is thdev: and 64 lowercase hex digits; a P-256 public key is thhw: and 66
lowercase hex digits (the 33-byte compressed point).
A phone’s signatures are ECDSA P-256 over SHA-256, as Android’s SHA256withECDSA makes them. They are stored and
sent as 64 raw bytes (r then s), normalised to low-S; DER from Android is accepted and converted.
A computer on your device list signs its commands to your other computers with its iroh key (Ed25519, 64 bytes,
checked strictly), the key the list already names for it, so nothing is connected again. It has no presence
key, so its commands carry no fingerprint: the computer that receives one skips the fingerprint rule for it and
keeps every other check. What it changes is reported to your phone (“Studio PC deleted a.txt on Cloud PC”). Only
your phone changes the device list.
What a command contains
Section titled “What a command contains”CommandBody { issuer: the iroh key of the phone or computer that sends it (32 bytes) target: the computer's iroh key (32 bytes) action: the encoded action (at most 4096 bytes) nonce: 16 random bytes issued_at: Unix seconds expires_at: Unix seconds}The keys sign "tealhand-cmd-v1" followed by the encoded body. The signed command carries the body, the
device signature and, when present, the presence signature. The phone gives each command a lifetime of
60 seconds; the computer accepts at most 300.
What the computer checks, in order
Section titled “What the computer checks, in order”- The connection’s peer (the key iroh proved in the handshake) is on the device list and not expired. A phone
must have a
devicekey; a computer is checked with its iroh key. - The
devicesignature is valid (a computer’s: its iroh key’s), and thepresencesignature too if there is one (a computer’s command may not have one). This happens before the body is read, with the key of the connection’s peer, not a key written in the body. - The body decodes strictly (one encoding only, no bytes left over), the action is at most 4096 bytes, and the lifetime is at most 300 seconds.
issueris the connection’s peer andtargetis this computer, so a command cannot be replayed from another device or to another computer.- The time:
now <= expires_at,issued_atat most 30 seconds in the future, andissued_atnot before this Tealhand Host started. - The nonce has not been seen from this issuer. It is then remembered until the command expires. One phone may
have at most 512 live nonces and all phones together 16,384; beyond that the answer is
Busy.
The nonce is recorded last, so a command that fails any check cannot use one up. A restart empties the nonce memory, which is why a command issued before the start is refused.
Only then does Tealhand Host read the action and check it again: parameters (validate), where it may come from
(a phone or another computer may not send local-only actions), the fingerprint rule (PresenceRequired; not
for a computer, which has no fingerprint key), and remote control. Each
step has its own error: Error codes.
The device list (roster)
Section titled “The device list (roster)”Roster { version: starts at 1, goes up by one with every change prev_hash: SHA-256 of the previous version's signed message (all zero for version 1) owner: the phone's device key issued_at: Unix seconds devices: [ { iroh key, name, kind (phone or computer), device key, presence key, added_at, expires_at (none = no expiry) } ]}- The owner key signs
"tealhand-roster-v1"followed by the encoded roster. The hash for the next version’sprev_hashis SHA-256 of that same message, so the signature’s encoding does not change the chain. - Connecting a phone and the phone itself use the strict rule: a new version is accepted only if it has the same owner, a
higher version and a
prev_hashthat matches the stored one. An older or forked list is refused. - A computer that gets a list pushed by the phone is more forgiving, so that it can catch up after missing
versions: any higher version from the same owner is accepted without a
prev_hashmatch (there is one owner key, so there are no forks), after the checks below. The same list again is answered Ok and changes nothing, so the phone can safely retry a push whose answer was lost. A list with a lower or equal version and a different hash is refused (Rollback). - At most 64 devices, and at most 32 KiB for the encoded list. Names follow the device name rules (1 to 64 characters, no control or invisible characters).
- A computer installs its first list only through a completed connection, after the click on the computer.
- A list pushed later by the phone may remove, rename or expire devices and add computers, but it may not
add a phone, change the kind or the keys of a device already listed, or give a new computer any keys. A new
phone only comes in through connecting, with a click on the computer, so a thief with an unlocked phone cannot
enrol their own key. The computer answers
NotAllowedand keeps its list. - When a valid next version no longer lists the computer, the computer forgets its saved connection and wipes its rules.
- When a device leaves the list, the computer closes its connections and cancels the rules it set.
Connection proofs
Section titled “Connection proofs”The QR code holds a one-time 16-byte secret.
- The phone’s hello carries an HMAC-SHA256 under the secret over a transcript of both iroh keys, the QR’s
expiry, both of the phone’s hardware keys and its name, starting with
"tealhand-pair-v1". The computer’s reply carries an HMAC over the hash of that transcript plus its name and OS. - The phone proves it holds its two hardware keys: both sign
"tealhand-bind-v1"followed by the hello transcript. - The check code is the first 8 characters, in Crockford base32, of SHA-256 over
"tealhand-pair-v1", the two iroh keys (smaller first) and the secret. Each device computes it itself; it is never sent or logged. - The computer accepts the device list from the phone only if it lists the computer’s own key and its owner is the key the hello announced, and it refuses a second owner.
So a person who photographed the QR code cannot connect silently (the computer asks for a click), and a man in the middle who swapped the QR code ends with a failed connection, not a takeover.