openapi: "3.1.0"
info:
  title: Qpher KEM API
  description: |
    Key Encapsulation Mechanism (KEM) operations — encrypt/decrypt data, encapsulate/decapsulate
    shared secrets, and wrap/unwrap symmetric keys using Kyber768 (ML-KEM-768) or X-Wing hybrid KEM.
  version: "1.1.0"
  contact:
    name: Qpher Support
    url: https://qpher.ai/support
    email: support@qpher.ai

servers:
  - url: https://api.qpher.ai
    description: Production

security:
  - apiKey: []

paths:
  /api/v1/kem/encrypt:
    post:
      operationId: kemEncrypt
      summary: Encrypt data using KEM-DEM
      description: |
        Encrypt plaintext using the hybrid KEM-DEM scheme:
        KEM encapsulate -> HKDF-SHA256 -> AES-256-GCM.
        The shared secret is ephemeral and never stored.
        Supports Kyber768 (default) and X-Wing hybrid algorithms.
      tags: [KEM]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [plaintext, key_version]
              properties:
                plaintext:
                  type: string
                  format: byte
                  description: Base64-encoded plaintext (max 1 MB)
                  example: SGVsbG8sIFF1YW50dW0gV29ybGQh
                key_version:
                  type: integer
                  minimum: 1
                  description: PQC key version (must be active)
                  example: 1
                algorithm:
                  type: string
                  enum: [Kyber768, X-Wing]
                  description: KEM algorithm. Default is Kyber768.
                  example: Kyber768
                mode:
                  type: string
                  enum: [standard, deterministic]
                  default: standard
                  description: Encryption mode. Deterministic requires salt.
                  example: standard
                salt:
                  type: string
                  format: byte
                  description: Base64-encoded salt (required if mode=deterministic)
                  example: c2FsdHZhbHVlMTIzNDU2Nzg=
      responses:
        "200":
          description: Encryption successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EncryptResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"

  /api/v1/kem/decrypt:
    post:
      operationId: kemDecrypt
      summary: Decrypt data using KEM-DEM
      description: |
        Decrypt ciphertext that was encrypted using the KEM-DEM scheme.
        Supports active and retired key versions.
        Supports Kyber768 (default) and X-Wing hybrid algorithms.
      tags: [KEM]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [ciphertext, key_version]
              properties:
                ciphertext:
                  type: string
                  format: byte
                  description: Base64-encoded ciphertext from encrypt
                  example: eyJrZW1fY2lwaGVydGV4dCI6Ii4uLiIsIm5vbmNlIjoiLi4uIiwiY2lwaGVydGV4dCI6Ii4uLiJ9
                key_version:
                  type: integer
                  minimum: 1
                  description: PQC key version (active or retired)
                  example: 1
                algorithm:
                  type: string
                  enum: [Kyber768, X-Wing]
                  description: KEM algorithm. Default is Kyber768.
                  example: Kyber768
      responses:
        "200":
          description: Decryption successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DecryptResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"

  /api/v1/kem/encapsulate:
    post:
      operationId: kemEncapsulate
      summary: KEM encapsulate (raw shared secret)
      description: |
        Perform a raw KEM encapsulation to produce a shared secret and KEM ciphertext.
        The caller receives both the shared secret and the KEM ciphertext.
        Use this when you want to derive your own encryption keys from the shared secret.
      tags: [KEM]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [key_version]
              properties:
                key_version:
                  type: integer
                  minimum: 1
                  description: Active key version to use
                  example: 1
                algorithm:
                  type: string
                  enum: [Kyber768, X-Wing]
                  description: KEM algorithm. Default is Kyber768.
                  example: Kyber768
      responses:
        "200":
          description: Encapsulation successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EncapsulateResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"

  /api/v1/kem/decapsulate:
    post:
      operationId: kemDecapsulate
      summary: KEM decapsulate (recover shared secret)
      description: |
        Perform a raw KEM decapsulation to recover the shared secret from KEM ciphertext.
        The private key never leaves the server — decapsulation happens server-side.
      tags: [KEM]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [kem_ciphertext, key_version]
              properties:
                kem_ciphertext:
                  type: string
                  format: byte
                  description: Base64-encoded KEM ciphertext from encapsulate
                  example: AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v
                key_version:
                  type: integer
                  minimum: 1
                  description: Key version (active or retired)
                  example: 1
                algorithm:
                  type: string
                  enum: [Kyber768, X-Wing]
                  description: KEM algorithm. Default is Kyber768.
                  example: Kyber768
      responses:
        "200":
          description: Decapsulation successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DecapsulateResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"

  /api/v1/kem/key/wrap:
    post:
      operationId: kemKeyWrap
      summary: Wrap a symmetric key using KEM-DEM
      description: |
        Wrap (encrypt) a symmetric key using the KEM-DEM scheme. The symmetric key
        must be 16, 24, 32, 48, or 64 bytes. Use this to protect AES keys, HMAC keys,
        or other symmetric material with post-quantum security.
      tags: [KEM]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [symmetric_key, key_version]
              properties:
                symmetric_key:
                  type: string
                  format: byte
                  description: Base64-encoded symmetric key to wrap (16/24/32/48/64 bytes)
                  example: MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=
                key_version:
                  type: integer
                  minimum: 1
                  description: Active key version to use for wrapping
                  example: 1
                algorithm:
                  type: string
                  enum: [Kyber768, X-Wing]
                  description: KEM algorithm. Default is Kyber768.
                  example: Kyber768
      responses:
        "200":
          description: Key wrapped successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KeyWrapResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"

  /api/v1/kem/key/unwrap:
    post:
      operationId: kemKeyUnwrap
      summary: Unwrap a symmetric key using KEM-DEM
      description: |
        Unwrap (decrypt) a symmetric key that was previously wrapped using kemKeyWrap.
        The private key never leaves the server — unwrapping happens server-side.
      tags: [KEM]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [wrapped_key, key_version]
              properties:
                wrapped_key:
                  type: string
                  format: byte
                  description: Base64-encoded wrapped key from key/wrap
                  example: eyJrZW1fY2lwaGVydGV4dCI6Ii4uLiIsIm5vbmNlIjoiLi4uIiwiY2lwaGVydGV4dCI6Ii4uLiJ9
                key_version:
                  type: integer
                  minimum: 1
                  description: Key version used for wrapping (active or retired)
                  example: 1
                algorithm:
                  type: string
                  enum: [Kyber768, X-Wing]
                  description: KEM algorithm. Default is Kyber768.
                  example: Kyber768
      responses:
        "200":
          description: Key unwrapped successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KeyUnwrapResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"

components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: X-API-Key

  schemas:
    EncryptResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            ciphertext:
              type: string
              format: byte
              description: Base64-encoded ciphertext
              example: eyJrZW1fY2lwaGVydGV4dCI6Ii4uLiIsIm5vbmNlIjoiLi4uIiwiY2lwaGVydGV4dCI6Ii4uLiJ9
            key_version:
              type: integer
              description: Key version used for encryption
              example: 1
            algorithm:
              type: string
              description: Algorithm used
              example: Kyber768
        request_id:
          type: string
          format: uuid
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
        timestamp:
          type: string
          format: date-time
          example: "2026-01-15T10:30:00.000Z"

    DecryptResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            plaintext:
              type: string
              format: byte
              description: Base64-encoded decrypted plaintext
              example: SGVsbG8sIFF1YW50dW0gV29ybGQh
            key_version:
              type: integer
              example: 1
            algorithm:
              type: string
              example: Kyber768
        request_id:
          type: string
          format: uuid
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
        timestamp:
          type: string
          format: date-time
          example: "2026-01-15T10:30:00.000Z"

    EncapsulateResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            kem_ciphertext:
              type: string
              format: byte
              description: Base64-encoded KEM ciphertext
              example: AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v
            shared_secret:
              type: string
              format: byte
              description: Base64-encoded 32-byte shared secret
              example: dGhpcyBpcyBhIDMyLWJ5dGUgc2hhcmVkIHNlY3JldCE=
            key_version:
              type: integer
              example: 1
            algorithm:
              type: string
              example: Kyber768
        request_id:
          type: string
          format: uuid
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
        timestamp:
          type: string
          format: date-time
          example: "2026-01-15T10:30:00.000Z"

    DecapsulateResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            shared_secret:
              type: string
              format: byte
              description: Base64-encoded 32-byte shared secret
              example: dGhpcyBpcyBhIDMyLWJ5dGUgc2hhcmVkIHNlY3JldCE=
            key_version:
              type: integer
              example: 1
            algorithm:
              type: string
              example: Kyber768
        request_id:
          type: string
          format: uuid
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
        timestamp:
          type: string
          format: date-time
          example: "2026-01-15T10:30:00.000Z"

    KeyWrapResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            wrapped_key:
              type: string
              format: byte
              description: Base64-encoded wrapped key (KEM-DEM ciphertext)
              example: eyJrZW1fY2lwaGVydGV4dCI6Ii4uLiIsIm5vbmNlIjoiLi4uIiwiY2lwaGVydGV4dCI6Ii4uLiJ9
            key_version:
              type: integer
              example: 1
            algorithm:
              type: string
              example: Kyber768
            wrapping_method:
              type: string
              description: Wrapping method used
              example: KEM-DEM
        request_id:
          type: string
          format: uuid
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
        timestamp:
          type: string
          format: date-time
          example: "2026-01-15T10:30:00.000Z"

    KeyUnwrapResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            symmetric_key:
              type: string
              format: byte
              description: Base64-encoded recovered symmetric key
              example: MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=
            key_version:
              type: integer
              example: 1
            algorithm:
              type: string
              example: Kyber768
        request_id:
          type: string
          format: uuid
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
        timestamp:
          type: string
          format: date-time
          example: "2026-01-15T10:30:00.000Z"

    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            error_code:
              type: string
              pattern: "^ERR_[A-Z_]+_\\d{3}$"
              description: Structured error code
              example: ERR_KEM_001
            message:
              type: string
              description: Human-readable error message
              example: Invalid key version
        request_id:
          type: string
          format: uuid
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
        timestamp:
          type: string
          format: date-time
          example: "2026-01-15T10:30:00.000Z"

  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Forbidden:
      description: Operation not allowed
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    RateLimited:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
