BYOiD/PROTOCOL

Tamper-Proof Authorization: VVK JWT Signing

How JWTs are threshold-signed by the VVK ORK swarm. Each VVK ORK independently verifies every claim against pre-approved authorization proofs before contributing partial signatures.

28 min read

Overview

This protocol specifies how JWTs are threshold-signed by the VVK ORK swarm after a user completes BYOiD authentication. The protocol has two architecturally distinct halves: the authentication preamble (standard OIDC + BYOiD, covered in Protocol: CMK Authentication) produces the user's authenticated identity; the VVK authorization ceremony verifies claims and threshold-signs the JWT.
The core architectural property: the VVK ORKs are independent verifiers, not blind signers. Before contributing a partial signature, each VVK ORK independently executes a 12-gate verification chain. It verifies the VRK delegation (proving TideCloak is authorized to communicate), the user's CMK authentication proof (cross-key verification linking the CMK domain to the VVK domain), validates the JWT draft's claims against the VVK-signed authorization proof, and rejects the request entirely if any claim exceeds what the proof authorizes. It further validates temporal binding (5-minute recency), session binding (JWT bound to gVenSessKeygVenSessKey), and identity matching (VUID consistency).
A compromised TideCloak cannot forge a JWT with unauthorized claims - it does not hold the VVK, and the VVK ORKs will reject any JWT whose claims are not a subset of the quorum-approved proof.
Loading diagram...

Preconditions

CategoryArtifactDescription
From BYOiDVendorEncryptedDataEncrypted package containing blindSign, AuthToken, VUID, optionally RagnarokEncrypted data. Produced by the Secure Web Enclave (SWE) during Protocol: CMK Authentication.
TideCloakVVID, gVVKVVK identifier and public key
VRK, gVRKVendor Random Key pair (delegated communication key)
gVRKSigVVK-signed proof of VRK delegation
SignedSettingsStatic vendor config (vURL, gVRK, Model, regOn, backupOn), signed
proofVVK-signed authorization proof for this user-client pair. Retrieved via DBlookup(proofDetails, RealmID, userID, clientID). Created during Protocol: Authorization Proofing.
JWTdraftDraft JWT containing all claims TideCloak would normally sign
Each VVK ORKVVK_iThis ORK's VVK shard
gVVKVVK public key
vSecORK_i, vgORK_iORK's key pair (derived from TideKey_i)
vAesORK_iORK's symmetric key (derived from TideKey_i)

Protocol Flow

Phase 1: Authentication Preamble (Reference Only)

The standard OIDC Authorization Code Flow enhanced by BYOiD. The user authenticates via PRISM + Double-Blind TSS against their CMK ORK swarm. The SWE produces VendorEncryptedData and redirects to TideCloak with an authorization code. At this point the user is authenticated but no JWT has been signed. See Protocol: CMK Authentication for the full specification.

Phases 2-4: VendorEncryptedData Decryption through preSignJWT

Loading diagram...
Phase 2 picks up after BYOiD. TideCloak receives VendorEncryptedData and decrypts it - the SWE encrypted the payload specifically for TideCloak's VRK during authentication. This ensures end-to-end transport confidentiality: a network adversary or malicious intermediary intercepting the browser redirect cannot extract the blindSign or session context.
Phase 3 is where TideCloak transitions from authentication acknowledgment to authorization drafting. It formulates the JWT payload from its local database (mapping the user to groups, roles, and scopes) and retrieves the VVK-signed authorization proof for this user-client pair. This proof is a cryptographic attestation created during the asynchronous IGA governance workflow - it defines exactly which claims this user-client pair is entitled to. TideCloak bundles the JWT draft, proof, and authentication artifacts in a signing request for the VVK ORKs.
Phase 4 is the core of Tamper-Proof Authorization. TideCloak first resolves the VVK ORK swarm via the TWELVE-MAP key lookup (conditional - if the roster is already in memory from a recent operation, the lookup is skipped). The TWELVE-MAP response provides the swarm's public key (gVVKgVVK), the list of ORK URLs (vORK[]vORK[]), their identifiers (vIdORKi[]vIdORK_i[]), and their public keys (vgORKi[]vgORK_i[]). TideCloak then establishes per-ORK ECDH channels using its VRK private key against each ORK's public key, producing unique pairwise secrets. The entire verification payload (blindSign, AuthToken, VUID, gCMKAuth, JWTdraft, proof) is encrypted individually for each ORK - no ORK can read another ORK's request.
TideCloak dispatches preSignJWT to all nn ORKs simultaneously. Each ORK independently executes Algorithm 1 - the 12-gate verification chain described below. Any single failed assertion causes the ORK to abort, drop the connection, and log an anomaly. Only ORKs where all 12 assertions pass generate a nonce and cache their state.
The cache is strictly bounded by two constraints. A 30-second TTL ensures that if TideCloak does not follow up with the SignJWT call within 30 seconds, the committed nonces are destroyed and the signing must restart from scratch. A maximum of 30 entries per VVID constrains the number of concurrent signing operations an attacker can attempt against a single vendor's VVK - limiting the adversary's probability space for Wagner birthday attacks. The cache stores three items per entry: the committed nonce private key (VVKRiVVKR_i), the ECDH session key (ECDHiECDH_i), and the verified JWT draft (JWTdraftJWTdraft), indexed by gVenSessKeygVenSessKey with VVIDVVID as a sub-index.

Phases 5-7: SignJWT through JWT Delivery

Loading diagram...
Phase 5: TideCloak waits for preSignJWT responses - optimistically for 1 second (hoping all nn respond) or up to 5 seconds to reach at least the threshold tt. It maps participation into the ORKsBitwiseORKsBitwise array and records the total responding count II. TideCloak decrypts the returned nonces using the ECDH session keys, then aggregates the public nonce points: gVVKR=(gVVKRi[1..I])gVVKR = \sum (gVVKR_i[1..I]). This aggregate nonce is the binding commitment that ties all ORKs to a single signing operation.
TideCloak then dispatches SignJWT to only the II participating ORKs (not all nn), providing the aggregate nonce, the participation bitmap, and the crypto model specification. Each ORK retrieves and permanently destroys its cached state (the 30-second window is consumed; the cache entry cannot be replayed). The ORK loads its VVKiVVK_i shard into ephemeral memory, serializes the previously verified JWTdraft according to the specified Model, and computes its partial threshold Ed25519 signature. The partialSign function takes seven parameters: the aggregate nonce (gVVKRgVVKR), the VVK public key (gVVKgVVK), the serialized JWT, the ORK's committed nonce (VVKRiVVKR_i), the ORK's VVK shard (VVKiVVK_i), the full ORK identity list (vIdORKi[1..n]vIdORK_i[1..n]), and the participation bitmap (ORKsBitwiseORKsBitwise). The participation bitmap is essential - it ensures every ORK computes its Lagrange interpolation coefficient against the same participant set. See Article 6: Authority in Action for the general threshold signing mechanism.
The partial signature is ECDH-encrypted for return to TideCloak. At no point does the VVK private key materialize - each ORK exercises its shard independently, and the partial signatures are designed to aggregate into a valid Ed25519Ed25519 signature without ever combining the key shares.
Phase 6: TideCloak waits up to 5 seconds for all II participating ORKs to return their partial signatures. It decrypts each using the ECDH session keys, then aggregates: JWTsigned=JWTsignedi[1..I]JWTsigned = \sum JWTsigned_i[1..I]. Before releasing the token, TideCloak performs a critical self-verification - it checks the assembled signature against the organization's public key: gVVK.verifySig(JWTsigned,gVVKR,JWTdraft)gVVK.\textsf{verifySig}(JWTsigned, gVVKR, JWTdraft). This catches any aggregation errors, corrupted partial signatures, or Byzantine ORK behavior. If valid, the JWT is canonically encoded with the header, payload, signature, and aggregate nonce according to the specified Model.

Refresh Token Authorization

When TideCloak issues a refresh token alongside the access token, it also requests a VVK threshold signature over a refresh authorization message containing the VUID, session ID, session expiry, and session key. This signature serves as a pre-authorization for future token refreshes: when the user's access token expires and TideCloak presents the refresh token, the VVK ORKs verify the refresh authorization signature instead of requiring a fresh BYOiD blind signature. The ORKs check that the refresh authorization was VVK-signed, that its expiry has not passed (with a 2-second buffer for clock skew), and that the session identifiers match. If verification passes, the ORKs proceed with the standard 12-gate verification using the refresh authorization as the authentication gate.
This avoids forcing a full BYOiD re-authentication on every token refresh while maintaining threshold verification. The refresh authorization is scoped to the SSO session lifetime and bound to the session key, preventing cross-session replay.

Default User Context Fallback

When TideCloak looks up the authorization proof for a user-client pair, it follows a two-level resolution: first, it queries for a user-specific access proof (a VVK-signed attestation created during the IGA governance workflow for this specific user and client). If no user-specific proof exists, it falls back to the client's default user context, a VVK-signed baseline authorization that applies to all users of that client. If neither a user-specific proof nor a default context exists, TideCloak cannot construct a valid signing request and the authenticated session is removed. No JWT is issued.
This fallback ensures that newly created clients have a functional authorization baseline before administrators have assigned user-specific roles, while still requiring every authorization to be backed by a VVK-signed proof.
Phase 7: Two delivery modes based on the vendor's security posture. The standard implementation returns the JWT through TideCloak to the Vendor via the normal OIDC Access Token Response. This path relies on TLS for transit security and exposes the signed token to TideCloak on the return path. The secure (non-standard) implementation encrypts the JWT under the vendor's session key (ENCgVenSessKey(JWT)\textsf{ENC}_{gVenSessKey}(JWT)), delivering directly from TideCloak to the Vendor. Only the Vendor holding venSessKeyvenSessKey can decrypt the token. This eliminates the TideCloak-to-Vendor leg as an attack surface - a compromised TideCloak on the delivery path cannot read, modify, or replay the token. The choice between modes is a deployment decision: the standard mode maintains full OIDC compatibility, while the secure mode trades compatibility for stronger transit guarantees.

Algorithms

Algorithm 1

Cross-key-population verification (step 4): The VVK ORKs (authorization domain) verify a signature produced by the CMK ORKs (authentication domain). This is the critical bridge between two entirely independent key populations: the user's identity is governed by CMK shards held by one set of ORKs, while the organization's authorization is governed by VVK shards held by a different set. The VVK ORKs have no access to CMK material - they verify the authentication proof using only the public verifier gCMKAuthgCMKAuth. A compromised TideCloak cannot simulate or bypass a user login because it cannot produce a valid blindSignblindSign without the user's CMK shares cooperating. Neither domain can be forged independently.
JWT claim validation (step 6): The ORK validates the JWT draft against the VVK-signed user context. Every claim in the draft must be a subset of what the user context authorizes. If a compromised TideCloak adds unauthorized claims to the JWT draft (elevated roles, expanded scopes, additional group memberships), the ORK detects the discrepancy and rejects the request entirely. The ORK does not modify or strip the JWT; it validates and either accepts or aborts. TideCloak constructs compliant JWT drafts by including only ACTIVE governance-approved roles and restricting audience to authorized clients. The ORK's validation is the enforcement backstop: even if TideCloak is fully compromised, unauthorized claims cannot survive the threshold verification because each ORK independently rejects non-compliant drafts.
Authorization proof (step 7): The proof was signed by the VVK during the IGA governance workflow after multi-admin quorum approval (Protocol: Authorization Proofing). The VVK ORKs treat it as ground truth: gVVK.verifySig(proof,bareJWT)gVVK.\textsf{verifySig}(proof, bareJWT) must hold, meaning the stripped JWT's claims must match exactly what the admin quorum approved for this user-client pair. Claims not covered by the proof are rejected regardless of what TideCloak requests. This is the closed loop between the two governance pillars - the proofs produced by quorum governance become the constraints enforced during real-time authorization.
Nonce commitment (step 12): The nonce VVKRiVVKR_i is generated fresh for every signing operation and committed before the ORK sees the aggregate nonce from other ORKs. This ordering is essential: in threshold Ed25519 signing, an adversary who observes nonces before choosing the message can exploit the birthday paradox (Wagner attack) to forge signatures. By committing nonces in the preSignJWT phase and only revealing the aggregate in the SignJWT phase, the protocol closes this attack vector.

The Wagner Birthday Attack Mitigation

Threshold Schnorr-based signatures face a known theoretical vulnerability: if an adversary controls the message and can observe nonces before choosing the message content, they can exploit the birthday paradox to forge signatures. This is relevant to the bulk signing workflow, where a single policy change may require the VVK ORKs to sign hundreds of updated proofs simultaneously.
Tide mitigates this through two mechanisms:
  1. Two-phase signing. All VVK threshold ceremonies use a strict commit-then-sign pattern. In the preSign phase, ORKs validate the request, verify all proofs, and commit their ephemeral signing nonces. These nonces are locked before the signing computation begins. Only after all committed nonces are aggregated by TideCloak does the Sign phase proceed, with each ORK producing its partial signature. The adversary cannot manipulate the message after observing nonces without invalidating the constraints established in the first phase. This same two-phase pattern (preSignJWT/SignJWT for authorization, preSignProofs/SignProofs for governance) is applied consistently across both pillars.
  2. The 30-signature cap. The protocol caps batch signing at 30 proofs per preSign/Sign round. If a governance change affects 3,000 user proofs, TideCloak paginates the workload into 100 isolated rounds. This constrains the mathematical probability space available for a birthday attack. Even if an adversary could control the message and observe nonces, they would have to generate a collision within a batch of 30 signatures, which is computationally infeasible with properly sized keys.

Notation Reference

SymbolDescription
VVKVVK, VVKiVVK_iVendor Verifiable Key / ORK ii's shard
gVVKgVVKVVK public key: GVVKG \cdot VVK
VVIDVVIDVVK identifier
VRKVRK, gVRKgVRKVendor Random Key pair (TideCloak's delegated communication key)
gVRKSiggVRKSigVVK-signed proof of VRK delegation
gVenSessKeygVenSessKey, venSessKeyvenSessKeyVendor's session key pair
VUIDVUIDVendor User ID: SHA512(gUserCMK).last32bytes\textsf{SHA512}(gUser \cdot CMK).\text{last32bytes}
gCMKAuthgCMKAuthUser's authentication verifier: gCMKCMKmulgCMK \cdot CMKmul
blindSignblindSignBlind signature from BYOiD authentication
AuthTokenAuthTokenAuthentication token (contains clientKeyclientKey, userIDuserID, expiryexpiry)
JWTdraftJWTdraftDraft JWT before VVK signing
proofproofVVK-signed authorization proof for user-client pair
bareJWTbareJWTJWT draft validated against user context (must be a subset of proof-authorized claims)
VVKRiVVKR_i, gVVKRigVVKR_iPer-ORK signing nonce (private / public)
gVVKRgVVKRAggregate nonce: aggregatePoints(gVVKRi[1..I])\textsf{aggregatePoints}(gVVKR_i[1..I])
JWTsignediJWTsigned_iORK ii's partial signature
JWTsignedJWTsignedAggregate signature: ΣJWTsignedi[1..I]\Sigma\, JWTsigned_i[1..I]
vSecORKivSecORK_i, vgORKivgORK_iVVK ORK ii's key pair
ECDHiECDH_iPairwise ECDH secret: vgORKi.DH(VRK)vgORK_i.\textsf{DH}(VRK)
ENCA(B)\textsf{ENC}_{A}(B)X25519-based El-Gamal encryption of message BB with public key AA
DECa(B)\textsf{DEC}_{a}(B)X25519-based El-Gamal encryption of message BB with private key aa
AES_ENCa(B)\textsf{AES\_ENC}_{a}(B)AES256 encryption of message BB with key aa
AES_DECa(B)\textsf{AES\_DEC}_{a}(B)AES256 decryption of message BB with key aa
ModelModelCrypto model specification (signature scheme)
ORKsBitwiseORKsBitwiseBitwise array of participating ORKs
nn, tt, IITotal ORKs (20) / threshold (14) / responding count

Actors and Trust Assumptions

ActorDescriptionTrust Assumption
UserEnd user who completed BYOiD authentication.Standard credentials; relies on SWE.
Vendor PlatformWeb application requesting authorization. Holds clientIDclientID. Initiates OIDC flow.Standard web app trust.
SWEBrowser agent. Orchestrates BYOiD, produces VendorEncryptedDataVendorEncryptedData.Potentially adversarial. Cannot forge BYOiD artifacts.
Home ORKEntry point for TWELVE-MAP key lookups. Provides VVK swarm roster.Routing only. No elevated trust.
CMK ORKs20 nodes holding user's CMK shards. Execute BYOiD (preceding protocol).Not directly involved in VVK signing. Their output is verified here.
TideCloakThe Authorization / OIDC server. Issues auth codes, manages sessions, drafts JWT. Aggregates partial signatures.Holds VRK, NOT VVK. Cannot alter claims - VVK ORKs verify independently. Can be fully compromised without enabling token forgery.
VVK ORKs20 nodes holding VVK shards. The signing authority. Each independently verifies all 12 conditions.Standard threshold trust (\leq13 may collude).
mimDBMimir Database - The Fabric's synced repository.Append-only integrity. Provides VVK swarm roster via TWELVE-MAP directory.
TWELVE-MAPTide Wide Enumerated Ledger of Verifiable Entries - Mapping Authoritative Pointers is the self-verifying directory service on mimDB mapping key identifiers to ORK swarms.Each entry includes a ZK proof attesting to its authenticity against the master registry.

Layer Traversal

LayerHow VVK JWT Signing Engages It
LegitimacyVRK delegation verification (gVVK.verifySig(gVRKSig)gVVK.\textsf{verifySig}(gVRKSig)). ECDH session establishment. Temporal binding (5-min recency, 30s cache TTL).
AuthorityVVK shard management. ORKs load VVKiVVK_i into ephemeral memory for partial signing.
AgencyPrimary layer. Threshold signing ceremony - each ORK contributes partialSign\textsf{partialSign} using its VVK shard. The Agency operation is JWT signing with independent claim verification.
SettlementSession binding via gVenSessKeygVenSessKey. Voucher validation for the signing operation.

Security Properties

PropertyMechanismAssumption
Claim verification against proofVVK ORKs verify gVVK.verifySig(proof,bareJWT)gVVK.\textsf{verifySig}(proof, bareJWT) and validate JWT claims are a subset of the VVK-signed user context. Non-compliant drafts are rejected entirely. Compromised TideCloak cannot inject unauthorized claims.Hash/signature collision resistance
Cross-key authenticationVVK ORKs verify gCMKAuth.verifySig(blindSign,AuthToken)gCMKAuth.\textsf{verifySig}(blindSign, AuthToken) - a CMK-domain signature verified in the VVK domain. Forged authentication → no JWT.DLP hardness
VRK delegation ≠ authorityVRK enables ECDH communication. Every ORK verifies gVRKSiggVRKSig before processing. Compromised VRK → can talk to ORKs but cannot pass the 12-gate verification.-
Wagner birthday mitigationTwo-phase commit: nonces committed in preSignJWT before signing in SignJWT. 30-entry cache limit per VVID constrains adversarial permutations.DLP hardness
Temporal bindingiatiat and auth_timeauth\_time within 5 minutes. AuthToken.expiryAuthToken.expiry in the future. 30s cache TTL. Prevents replay of stale artifacts.ORK NTP accuracy
Session bindingsession_statesession\_state, sidsid, spkspk must match gVenSessKeygVenSessKey and AuthToken.clientKeyAuthToken.clientKey. Prevents cross-session token replay.-
Independent ORK verificationEach of 20 ORKs verifies all 12 conditions independently. No ORK trusts another's result.Node independence
Threshold signing14-of-20 must agree. Compromising ≤13 is insufficient.Threshold assumption
No key reconstructionVVK never materializes. Partial signatures aggregated; key exercised but never assembled.-
Transport encryptionAll ORK communication ECDH-encrypted (VRKvSecORKiVRK \leftrightarrow vSecORK_i). Independent of TLS.ECDH + AES security
Endpoint confidentiality (secure mode)JWT encrypted under gVenSessKeygVenSessKey. Only the Vendor holding venSessKeyvenSessKey can read the token.-

Call Summary

CallDirectionPurpose
DBlookupTideCloak → Local DBRetrieve VVK-signed authorization proof for user-client pair.
keyLookupTideCloak → Home ORKResolve VVK ORK swarm roster (gVVKgVVK, vORK[]vORK[], vIdORKi[]vIdORK_i[], vgORKi[]vgORK_i[]).
preSignJWTTideCloak → all nn VVK ORKsPhase 1 commitment. Delivers JWT draft, proof, and CMK auth artifacts. Each ORK executes Algorithm 1 (12-gate verification) and commits nonce.
SignJWTTideCloak → II participating ORKsPhase 2 execution. Delivers aggregate nonce and triggers partialSign\textsf{partialSign} over verified JWT.

References

  • Hall, J. L., Hertzog, Y., et al. (2023). Manifesting Unobtainable Secrets: Threshold Elliptic Curve Key Generation using Nested Shamir Secret Sharing. arXiv:2309.00915. Presented at AustMS 2021.
  • Komlo, C. & Goldberg, I. (2020). FROST: Flexible Round-Optimized Schnorr Threshold Signatures. SAC 2020.
  • Wagner, D. (2002). A Generalized Birthday Problem. CRYPTO 2002.
  • Tide Developer Documentation: docs.tidecloak.com
Related Protocol Articles: