openapi: "3.1.0"
info:
  title: Qpher Signature API
  description: |
    Digital signature operations — sign/verify messages and pre-computed hashes
    using Dilithium3 (ML-DSA-65) or Composite-ML-DSA (ECDSA P-256 + ML-DSA-65).
  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/signature/sign:
    post:
      operationId: signatureSign
      summary: Sign a message
      description: |
        Create a digital signature for the provided message using
        Dilithium3 (ML-DSA-65) or Composite-ML-DSA. Requires an active key version.
      tags: [Signatures]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [message, key_version]
              properties:
                message:
                  type: string
                  format: byte
                  description: Base64-encoded message to sign
                  example: SGVsbG8sIFF1YW50dW0gV29ybGQh
                key_version:
                  type: integer
                  minimum: 1
                  description: PQC key version (must be active)
                  example: 1
                algorithm:
                  type: string
                  enum: [Dilithium3, Composite-ML-DSA]
                  description: Signature algorithm. Default is Dilithium3.
                  example: Dilithium3
      responses:
        "200":
          description: Signing successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SignResponse"
        "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/signature/verify:
    post:
      operationId: signatureVerify
      summary: Verify a signature
      description: |
        Verify a digital signature against the original message.
        Supports active and retired key versions.
        Supports Dilithium3 (default) and Composite-ML-DSA algorithms.
      tags: [Signatures]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [message, signature, key_version]
              properties:
                message:
                  type: string
                  format: byte
                  description: Base64-encoded original message
                  example: SGVsbG8sIFF1YW50dW0gV29ybGQh
                signature:
                  type: string
                  format: byte
                  description: Base64-encoded signature to verify
                  example: TUlJRHNpZ25hdHVyZV9leGFtcGxlX2Jhc2U2NF9wbGFjZWhvbGRlcl92YWx1ZQ==
                key_version:
                  type: integer
                  minimum: 1
                  description: PQC key version (active or retired)
                  example: 1
                algorithm:
                  type: string
                  enum: [Dilithium3, Composite-ML-DSA]
                  description: Signature algorithm. Default is Dilithium3.
                  example: Dilithium3
      responses:
        "200":
          description: Verification complete
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VerifyResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"

  /api/v1/signature/sign-hash:
    post:
      operationId: signatureSignHash
      summary: Sign a pre-computed hash
      description: |
        Sign a pre-computed hash digest instead of the full message.
        Useful for large files or streaming scenarios where you compute
        the hash client-side. Note: Dilithium internally re-hashes the input,
        so sign-hash output differs from sign(hash(message)).
      tags: [Signatures]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [hash, hash_algorithm, key_version]
              properties:
                hash:
                  type: string
                  format: byte
                  description: Base64-encoded hash digest
                  example: "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="
                hash_algorithm:
                  type: string
                  enum: [SHA-256, SHA-384, SHA-512]
                  description: Hash algorithm used to compute the digest
                  example: SHA-256
                key_version:
                  type: integer
                  minimum: 1
                  description: PQC key version (must be active)
                  example: 1
                algorithm:
                  type: string
                  enum: [Dilithium3, Composite-ML-DSA]
                  description: Signature algorithm. Default is Dilithium3.
                  example: Dilithium3
      responses:
        "200":
          description: Hash signing successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SignHashResponse"
        "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/signature/verify-hash:
    post:
      operationId: signatureVerifyHash
      summary: Verify a hash-based signature
      description: |
        Verify a signature that was created using sign-hash against the
        same pre-computed hash digest. The hash algorithm and length must match.
      tags: [Signatures]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [hash, hash_algorithm, signature, key_version]
              properties:
                hash:
                  type: string
                  format: byte
                  description: Base64-encoded hash digest
                  example: "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="
                hash_algorithm:
                  type: string
                  enum: [SHA-256, SHA-384, SHA-512]
                  description: Hash algorithm used to compute the digest
                  example: SHA-256
                signature:
                  type: string
                  format: byte
                  description: Base64-encoded signature from sign-hash
                  example: TUlJRHNpZ25hdHVyZV9leGFtcGxlX2Jhc2U2NF9wbGFjZWhvbGRlcl92YWx1ZQ==
                key_version:
                  type: integer
                  minimum: 1
                  description: PQC key version (active or retired)
                  example: 1
                algorithm:
                  type: string
                  enum: [Dilithium3, Composite-ML-DSA]
                  description: Signature algorithm. Default is Dilithium3.
                  example: Dilithium3
      responses:
        "200":
          description: Hash verification complete
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VerifyHashResponse"
        "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:
    SignResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            signature:
              type: string
              format: byte
              description: Base64-encoded signature (Dilithium3 / ML-DSA-65 is 3,309 bytes raw per FIPS 204)
              example: TUlJRHNpZ25hdHVyZV9leGFtcGxlX2Jhc2U2NF9wbGFjZWhvbGRlcl92YWx1ZQ==
            key_version:
              type: integer
              example: 1
            algorithm:
              type: string
              example: Dilithium3
        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"

    VerifyResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            valid:
              type: boolean
              description: Whether the signature is valid
              example: true
            key_version:
              type: integer
              example: 1
            algorithm:
              type: string
              example: Dilithium3
        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"

    SignHashResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            signature:
              type: string
              format: byte
              description: Base64-encoded signature over the hash
              example: TUlJRHNpZ25hdHVyZV9leGFtcGxlX2Jhc2U2NF9wbGFjZWhvbGRlcl92YWx1ZQ==
            key_version:
              type: integer
              example: 1
            algorithm:
              type: string
              example: Dilithium3
            hash_algorithm:
              type: string
              example: SHA-256
            signature_type:
              type: string
              description: Always "detached" for hash-based signatures
              example: detached
        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"

    VerifyHashResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            valid:
              type: boolean
              description: Whether the hash signature is valid
              example: true
            key_version:
              type: integer
              example: 1
            algorithm:
              type: string
              example: Dilithium3
            hash_algorithm:
              type: string
              example: SHA-256
        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}$"
              example: ERR_SIG_001
            message:
              type: string
              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"
