Detailed Component Design

Overview

This page provides component-level implementation details for ZeroVerify, including database schemas, API specifications, and algorithm logic for the core cryptographic operations.

Database Schema

Credential Metadata Table (DynamoDB)

The credential metadata table tracks issued credentials and their revocation status. Each record uniquely identifies a credential with its expiration and revocation index.

AttributeTypeRoleDescription
subjectStringPartition keyPseudonymous user identifier (HMAC of issuer_id || sub_id)
credential_idString (UUID)Sort keyUnique credential identifier
issue_dateString (ISO 8601)AttributeTimestamp when credential was issued
expiry_dateString (ISO 8601)AttributeTimestamp when credential expires
revocation_indexNumberAttributeBit position in W3C Status List bitstring
statusStringAttributeACTIVE or REVOKED

Credential Entity Schema

The credential itself is a W3C Verifiable Credential containing user attributes, signed with BBS+ signature. Stored locally in user's browser IndexedDB.

{
  "student_id": "G89u28394",
  "email": "anton@oakland.edu",
  "first_name": "Anton",
  "last_name": "Sakhanovych",
  "birthdate": "2000-05-15",
  "custom_claims": {
    "student_status": "enrolled",
    "enrollment_date": "2021-09-01",
    "university": "Oakland University",
    "major": "Computer Science"
  }
}

Proof Entity Schema

The proof structure generated by the client and sent to the verifier for validation.

{
  "proof_type": "age_over_21",
  "proof": "base64_zkSNARK_proof_bytes",
  "public_inputs": {
    "challenge": "n-0S6_WzA2Mj",
    "credential_status_index": 94567
  }
}

API Specification

Credential Issuance Endpoint

POST /api/credentials/issue

Issues a BBS+ signed credential after OAuth authentication. Returns signed credential to client for local storage.

Request Body

{
  "authorization_code": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "redirect_uri": "https://app.zeroverify.com/callback"
}

Response (200 Success)

{
  "credential": {
    "@context": ["https://www.w3.org/2018/credentials/v1"],
    "type": ["VerifiableCredential", "StudentCredential"],
    "issuer": "did:web:api.zeroverify.com",
    "issuanceDate": "2025-02-10T20:41:27Z",
    "expirationDate": "2026-02-10T20:41:27Z",
    "credentialSubject": {
      "id": "did:key:z6MkF5rGMoatr...",
      "student_status": "enrolled",
      "university": "Oakland University",
      "enrollment_date": "2021-09-01"
    },
    "proof": {
      "type": "BbsBlsSignature2020",
      "created": "2025-02-10T20:41:27Z",
      "proofPurpose": "assertionMethod",
      "verificationMethod": "did:web:api.zeroverify.com#key-1",
      "proofValue": "base64_bbs_signature_bytes..."
    }
  },
  "credential_id": "a3f8b2c1-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
  "revocation_index": 94567
}

Error States

400 Bad Request - Invalid authorization code

{
  "error": "invalid_request",
  "error_description": "Authorization code is invalid or expired"
}

401 Unauthorized - OAuth verification failed

{
  "error": "unauthorized",
  "error_description": "OAuth token verification failed"
}

409 Conflict - Active credential already exists

{
  "error": "duplicate_credential",
  "error_description": "An active credential already exists for this user"
}

500 Internal Server Error - Credential signing failed

{
  "error": "internal_error",
  "error_description": "Failed to generate credential signature"
}

Revocation Endpoint

POST /api/credentials/revoke

Revokes a credential by updating its status in DynamoDB and flipping the corresponding bit in the W3C Status List bitstring.

Request Body

{
  "credential_id": "a3f8b2c1-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
  "proof_of_ownership": "base64_zk_proof_of_credential_ownership"
}

Response (200 Success)

{
  "status": "revoked",
  "credential_id": "a3f8b2c1-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
  "revoked_at": "2025-03-15T14:22:00Z"
}

Error States

404 Not Found - Credential does not exist

{
  "error": "not_found",
  "error_description": "Credential ID not found"
}

403 Forbidden - Proof of ownership invalid

{
  "error": "forbidden",
  "error_description": "Invalid proof of credential ownership"
}

Algorithm Logic

Credential Issuance Flow

1. User initiates issuance from React web app
2. App redirects to Keycloak
3. Keycloak federates with institution's IdP via SAML/OAuth
4. User authenticates with institution
5. Keycloak returns authorization code to React app
6. React app sends code to Issuance Lambda via API Gateway

Lambda execution:
7. Exchange authorization code with Keycloak for OIDC claims
8. Extract verified attributes from claims (student_status, email, etc.)
9. Compute pseudonymous subject ID: HMAC(issuer_id || sub_id)
10. Query DynamoDB for existing active credential
   IF active credential exists:
     RETURN 409 Conflict
11. Generate new credential_id (UUID)
12. Assign next available revocation_index
13. Create W3C Verifiable Credential structure
14. Load BBS+ private key from AWS Secrets Manager
15. Sign credential with BBS+ signature
16. Write credential metadata to DynamoDB:
    - subject: pseudonymous_id
    - credential_id: new_uuid
    - status: ACTIVE
    - issue_date, expiry_date, revocation_index
17. Return signed credential to React app
18. React app stores credential in browser IndexedDB
19. Credential never transmitted to any server again

ZK Proof Generation Flow (Client-Side)

1. Verifier generates verification URL:
   https://verify.zeroverify.com/?proof_type=student_status
     &callback=https://merchant.com/verify
     &nonce=random_challenge_string

2. User opens URL in browser
3. React app parses query parameters
4. Load credential from IndexedDB
5. Check credential validity:
   - Expiration date > current time
   - Status = ACTIVE (check local cache or fetch bitstring)
6. Display consent screen to user:
   - Verifier: merchant.com
   - Proof type: "Student Status"
   - What will be disclosed: "Valid/Invalid (no personal data)"
7. IF user denies:
     RETURN consent_denied to callback
8. IF user approves:

   Client-side proof generation:
   9. Load zk-SNARK circuit for proof_type from S3
   10. Prepare circuit inputs:
       - Private inputs: credential attributes (student_status, birthdate, etc.)
       - Public inputs: challenge nonce, revocation_index
   11. Execute Groth16 prover (compiled to WebAssembly):
       - Circuit proves: "credential.student_status == 'enrolled'"
       - Without revealing actual student_status value
   12. Generate zk-SNARK proof (2-5 seconds)
   13. Construct proof object:
       {
         proof_type: "student_status",
         proof: base64_proof_bytes,
         public_inputs: { challenge: nonce, credential_status_index: 94567 }
       }
   14. POST proof directly to verifier's callback endpoint
   15. Display result to user: "Verification sent"

Proof Verification Logic (Verifier-Side)

Verifier receives proof at their callback endpoint:

1. Extract proof components:
   - proof_type
   - proof (zkSNARK proof bytes)
   - public_inputs (challenge, credential_status_index)

2. Validation checks:

   a) Challenge nonce verification:
      IF public_inputs.challenge != expected_nonce:
        RETURN invalid (replay attack or wrong session)

   b) Challenge freshness:
      IF challenge_timestamp > 5 minutes ago:
        RETURN invalid (expired challenge)

   c) Challenge single-use:
      IF challenge already used:
        RETURN invalid (replay attack)
      ELSE:
        Mark challenge as used

   d) Cryptographic proof verification:
      - Load verification key for proof_type from ZeroVerify S3
      - Run Groth16 verifier:
          verify(proof, public_inputs, verification_key)
      IF verification fails:
        RETURN invalid (tampered or incorrect proof)

   e) Revocation check:
      - Download W3C Status List bitstring from ZeroVerify S3
      - Extract bit at position: public_inputs.credential_status_index
      IF bit == 1:
        RETURN invalid (credential revoked)

3. All checks passed:
   RETURN valid

4. Verifier makes business decision:
   - Grant student discount
   - Allow access
   - Log verification event (no PII, only: timestamp, proof_type, result)

Component Interactions

System Flow Summary

Issuance Path

React App ↔ Keycloak ↔ Institution IdP
React App → API Gateway → Issuance Lambda → DynamoDB
Issuance Lambda ← AWS Secrets Manager (BBS+ key)
React App ← Signed Credential → IndexedDB (local storage)

Verification Path

Verifier → User (verification URL)
React App ← IndexedDB (load credential)
React App ← S3 (load circuit & verification key)
React App → Verifier Callback (POST proof)
Verifier ← S3 (verification key & bitstring)
Verifier → Business Logic (grant/deny access)

Revocation Path

User → API Gateway → SQS Queue
Revocation Lambda ← SQS (batch processing)
Revocation Lambda ↔ DynamoDB (update status)
Revocation Lambda ↔ S3 (update bitstring)