> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# SIP digest verification model

> Three roles of SIP authentication — HA1 verification at the Jambonz edge, an optional local re-check for cap counting, and the softswitch as the authority on registration — and why the local check deliberately fails open on digests it cannot recompute.

# SIP digest verification model

When a SIP device registers with Devotel Orbit, the digest handshake is verified at more than one layer. Reading any single layer in isolation is misleading — an operator who finds the local registration-cap check can misread its fail-open branch as a security hole. This page lays out the roles each verification layer plays, which digest envelopes the local check can and cannot recompute, and why falling open on the un-recomputable envelopes is the correct design. The credential the handshake authenticates is covered end to end in [SIP credential lifecycle](/concepts/sip-credential-lifecycle).

## The three roles of SIP authentication

SIP registration authentication in Orbit plays three distinct roles, each at a different layer:

1. **Edge verification — prove the password before the binding forms.** When a device sends `REGISTER`, the Jambonz softswitch challenges with a nonce and re-computes the digest `response` against the stored HA1 (the RFC 2617 A1 hash over `username:realm:password`, kept in an MD5 and a SHA-256 variant). A bad response gets a 401/403 and no binding ever forms. A stolen username alone cannot register.
2. **Optional local re-check — keep bogus User-Agents out of the cap.** A credential with a [registration cap](/concepts/sip-credential-lifecycle#per-credential-caps) re-verifies the digest locally before the slot is counted, so an attacker who knows only the username cannot fill the cap with junk endpoints and lock out your real devices. This check is accounting, not an auth gate — and this page documents exactly when it abstains.
3. **Softswitch as authority — the last word on registration.** The softswitch re-verifies the digest downstream regardless of anything Orbit's local layer concluded. The local layer can only ever decide whether a cap slot is counted; it can never admit a registration the softswitch would reject, and it never overrides a rejection.

## The digest algorithm envelope

The local re-check recomputes the expected `response` from the same stored HA1 it hands the softswitch, using the algorithm the device announced:

* **MD5 or SHA-256 with `qop=auth` (or RFC 2069 no-qop)** — fully recomputable. The expected response is rebuilt over `(HA1, nonce, nc, cnonce, qop, HA2)` and compared in constant time. A mismatch means the digest is wrong *and* the slot is not counted.
* **`*-sess` variants (MD5-sess, SHA-256-sess)** — the sess HA1 folds the nonce and client nonce into the A1 hash itself. Orbit stores the base HA1 only, so the sess variant cannot be reconstructed locally. The check abstains and the slot is not counted.
* **`qop=auth-int`** — needs a hash of the SIP message body, which the local hook does not receive. The check abstains and the slot is not counted.
* **Missing digest fields (nonce, URI, response) or an unrecognized algorithm** — nothing to recompute against. Abstain and do not count the slot.

Every abstention has the same outcome: the registration proceeds to the softswitch for the real verification, and the cap simply does not reserve a slot for that attempt.

## Why fail-open is correct

The fail-open posture follows directly from the split of roles above. The local check exists to keep the cap a fair accounting of verified registrations — it is a guardrail, not a gate. If it failed *closed* on digests it cannot recompute, any device legitimately negotiating a `*-sess` algorithm or `auth-int` quality-of-protection would be turned away from a cap it should pass, and nothing the softswitch decided afterwards could un-block it.

Failing open cannot admit an attacker. A bogus digest on a `sess` algorithm skips the slot accounting and reaches the softswitch, which re-verifies against the stored HA1 and rejects it with a 401/403 — the same outcome the edge challenge produces for any bad digest. The worst case of an abstention is a registration whose slot was not counted, while the binding still has to survive the authoritative verification. The worst case of failing closed would be turning away your own phones.

## Which envelopes must fall open

The platform's outbound and registration boundary is anchored by a single rule — inbound termination stays at the edge, and the softswitch remains the authority on what registers. That rule fixes precisely which digest envelopes the local check is allowed to abstain on rather than misjudge:

* **`*-sess` algorithms** — abstain; the session-HA1 variant is provably un-recomputable from the stored base HA1.
* **`qop` values other than `auth`** (today `auth-int`) — abstain; the entity-body hash is not available at the hook.
* **Missing nonce, URI, or response fields** — abstain; there is no well-formed digest to compare.

Envelopes outside that list — MD5 or SHA-256 with `qop=auth`, or RFC 2069 with no `qop` — must verify exactly, with the comparison in constant time. When you review or extend the digest handling, keep the abstention list complete: an envelope added to the digest path without falling into one of these classes is the defect case, and a `*-sess` or `auth-int` attempt *blocked* by the local check is the regression this page exists to prevent.

## Where the handshake terminates

Every REGISTER — recomputable or abstained — still terminates on the Devotel wholesale softswitch, which re-verifies the digest against the stored HA1 before any binding is accepted. The local envelope rules above only shape how the registration cap accounts for attempts; they never reroute, bypass, or override that termination. The edge handshake and the error statuses it surfaces (403 / 402 / 480) are described in [Jambonz softswitch model](/concepts/jambonz-softswitch).
