openapi: "3.1.0"
info:
  title: CipherHUB Cryptography API
  version: "1.2.0"
  description: |
    CipherHUB exposes 31 cryptographic operations covering classical algorithms (AES, RSA, ECC, SM2/3/4, ZUC),
    post-quantum cryptography (ML-KEM FIPS 203, ML-DSA FIPS 204, Hybrid KEX), and utility functions.

    **Recommended integration path for AI Agents**: use the MCP endpoint `POST https://tools.cipherhub.cloud/cipherhub/mcp`.
    MCP clients do not need to implement the CipherHUB application-layer encrypted channel.

    **Encrypted REST API**: `POST https://tools.cipherhub.cloud/cipherhub/api` is not a plaintext business JSON API.
    The REST request body must be wrapped by the CipherHUB SDK channel protocol before transmission.
    The plaintext business object containing `request_id`, `action`, and action-specific parameters is encrypted locally,
    then sent as an `EncryptedApiRequest`.

    **SDK channel protocol summary**:
    1. Call `GET /cipherhub/get_protection_key` to fetch a server protection key.
    2. Generate an ephemeral ECC key pair on the client side.
    3. Run ECDH with the server public key and derive a 32-byte symmetric key with HKDF-SHA512.
    4. Encrypt the plaintext business JSON with ChaCha20-Poly1305 and a fresh 12-byte nonce.
    5. Sign the plaintext business JSON with HMAC-SHA512 using the derived symmetric key.
    6. Submit `key_id`, encrypted data, nonce, ephemeral public key, signature, timestamp, and request_id to `POST /cipherhub/api`.
    7. Decrypt the encrypted response with the same symmetric key and verify its HMAC-SHA512 signature.

    HTTPS protects the transport layer. The CipherHUB SDK channel adds application-layer end-to-end protection.
    Do not send plaintext business requests directly to `/cipherhub/api`. Use `sdk/client_request.py` where possible.

    This document serves as a capability declaration for AI Agents and developers to understand
    available operations, parameter schemas, encrypted-channel wrapping, and response formats.

    **Channel implementation reference**: For a complete Python reference implementation of the encrypted
    channel protocol (ECDH + HKDF-SHA512 + ChaCha20-Poly1305 + HMAC-SHA512), query MCP resource
    `cipherhub://channel-implementation` via `resources/read`, or read `docs/channel-implementation-guide.md`.
  contact:
    name: Yang X. CHEN
    url: https://cipherhub.cloud
  license:
    name: Proprietary
    url: https://cipherhub.cloud

servers:
  - url: https://tools.cipherhub.cloud
    description: Production (MCP + REST)

paths:
  /cipherhub/mcp:
    post:
      summary: MCP Streamable HTTP Endpoint
      description: JSON-RPC 2.0 endpoint for Model Context Protocol. Supports tools/list, tools/call, prompts/list, prompts/get, resources/list, resources/read. This is the recommended path for AI Agents because it does not require implementing the CipherHUB REST encrypted channel.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [jsonrpc, method, id]
              properties:
                jsonrpc:
                  type: string
                  const: "2.0"
                method:
                  type: string
                  enum: [tools/list, tools/call, prompts/list, prompts/get, resources/list, resources/read]
                params:
                  type: object
                id:
                  type: [string, integer]
      responses:
        "200":
          description: JSON-RPC 2.0 response

  /cipherhub/get_protection_key:
    get:
      summary: Get REST channel protection key
      description: Returns the server ECC ECDH protection public key. Clients need this key before constructing an encrypted REST request for `/cipherhub/api`. Public Nginx route `/cipherhub/get_protection_key` maps to backend route `/get_protection_key`.
      responses:
        "200":
          description: Server protection key for the encrypted REST channel
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProtectionKeyResponse"

  /cipherhub/get_api_list:
    get:
      summary: Get supported REST business actions
      description: Returns all supported plaintext business `action` names. These action names are used inside the encrypted business JSON before SDK channel wrapping. Public Nginx route `/cipherhub/get_api_list` maps to backend route `/get_api_list`.
      responses:
        "200":
          description: Supported business action list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiListResponse"

  /cipherhub/api:
    post:
      summary: Encrypted REST business API
      description: |
        Application-layer encrypted REST endpoint. The request body must be `EncryptedApiRequest`, not plaintext business JSON.

        Plaintext business JSON has the shape `{ "request_id": "...", "action": "HashSum", ... }`, but it must be encrypted with the CipherHUB SDK channel before being sent here.

        Use `sdk/client_request.py` or implement the documented ECDH + HKDF-SHA512 + ChaCha20-Poly1305 + HMAC-SHA512 wrapping protocol.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EncryptedApiRequest"
      responses:
        "200":
          description: Encrypted business response, or a plaintext error response if channel processing failed
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/EncryptedApiResponse"
                  - $ref: "#/components/schemas/CommonErrorResponse"

components:
  schemas:
    # ── REST encrypted channel envelope ──────────────────────
    ProtectionKeyResponse:
      type: object
      required: [key_id, public_key, algorithm]
      properties:
        key_id:
          type: string
          description: Server protection key identifier used in `EncryptedApiRequest.key_id`.
        public_key:
          type: string
          description: Server ECC ECDH public key in PEM format.
        algorithm:
          type: string
          enum: [ECC_ECDH]
          description: Protection-key algorithm. Current value is `ECC_ECDH`.
      examples:
        - key_id: "b8b2c6f6c4a64e39a2a2f5e4f1a0c9d0"
          public_key: "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n"
          algorithm: ECC_ECDH

    ApiListResponse:
      type: array
      description: List of supported plaintext business action names used before encrypted-channel wrapping.
      items:
        type: string
      examples:
        - [Hello, GenerateRandomData, HashSum, HmacSum, BlockCipher, StreamCipher, MLKEMKeyGen, MLDSASign]

    CipherHubBusinessRequest:
      type: object
      required: [request_id, action]
      additionalProperties: true
      description: |
        Plaintext business request before SDK channel wrapping. This object is never sent directly to `/cipherhub/api`.
        Serialize it as UTF-8 JSON, encrypt it with the SDK channel, then place the result in `EncryptedApiRequest.encrypted_data`.
      properties:
        request_id:
          type: string
          minLength: 8
          maxLength: 64
          description: Client-generated request identifier.
        action:
          type: string
          description: Business action name returned by `/cipherhub/get_api_list`.
      examples:
        - request_id: "demo-request-0001"
          action: HashSum
          required_hash_modes: [Sha256]
          plain_in_hex: "68656c6c6f"
        - request_id: "demo-request-0002"
          action: GenerateRandomData
          data_length: 32

    EncryptedApiRequest:
      type: object
      required: [key_id, encrypted_data, encrypted_key, signature, timestamp, request_id]
      description: |
        REST encrypted-channel request body. `encrypted_data` contains the ChaCha20-Poly1305 ciphertext.
        New clients also send `nonce` as a separate 12-byte hex string. For legacy compatibility, `encrypted_data`
        may contain `ciphertext || nonce`; the server accepts both forms.
      properties:
        key_id:
          type: string
          description: Protection key identifier returned by `/cipherhub/get_protection_key`.
        encrypted_data:
          type: string
          pattern: "^[0-9a-fA-F]+$"
          description: ChaCha20-Poly1305 ciphertext as hex. Legacy clients append the 12-byte nonce to this field.
        nonce:
          type: [string, "null"]
          pattern: "^[0-9a-fA-F]{24}$"
          description: 12-byte ChaCha20-Poly1305 nonce as hex. If omitted, the server extracts nonce from the last 12 bytes of `encrypted_data`.
        encrypted_key:
          type: string
          pattern: "^[0-9a-fA-F]+$"
          description: Client ephemeral ECC public key encoded as hex.
        signature:
          type: string
          pattern: "^[0-9a-fA-F]{128}$"
          description: HMAC-SHA512 over the plaintext business JSON, encoded as hex.
        timestamp:
          type: string
          format: date-time
          description: Client send time in ISO 8601 format.
        request_id:
          type: string
          minLength: 8
          maxLength: 64
          description: Client-generated request identifier. It should match the plaintext business request `request_id`.
      examples:
        - key_id: "b8b2c6f6c4a64e39a2a2f5e4f1a0c9d0"
          encrypted_data: "6f2a...d91c"
          nonce: "00112233445566778899aabb"
          encrypted_key: "3059..."
          signature: "9f1d..."
          timestamp: "2026-04-30T10:31:00"
          request_id: "demo-request-0001"

    EncryptedApiResponse:
      type: object
      required: [encrypted_data, nonce, signature]
      description: Encrypted REST response. Decrypt `encrypted_data` with the same derived symmetric key and verify HMAC-SHA512 over the plaintext response JSON.
      properties:
        encrypted_data:
          type: string
          pattern: "^[0-9a-fA-F]+$"
          description: ChaCha20-Poly1305 response ciphertext as hex.
        nonce:
          type: string
          pattern: "^[0-9a-fA-F]{24}$"
          description: 12-byte response nonce as hex.
        signature:
          type: string
          pattern: "^[0-9a-fA-F]{128}$"
          description: HMAC-SHA512 over the plaintext response JSON, encoded as hex.

    CommonErrorResponse:
      type: object
      required: [request_id, code, msg]
      description: Plaintext error response returned when encrypted-channel processing fails before a response can be encrypted.
      properties:
        request_id:
          type: string
        code:
          type: string
        msg:
          type: string

    # ── Hash & HMAC ──────────────────────────────────────────
    HashSumRequest:
      type: object
      required: [hash_mode, message_in_hex]
      properties:
        hash_mode:
          type: string
          enum: [Sha1, Sha224, Sha256, Sha384, Sha512, Sm3, Shake128, Shake256]
          description: Hash algorithm to use
        message_in_hex:
          type: string
          description: Input message as hex string

    HmacSumRequest:
      type: object
      required: [hash_mode, key_in_hex, message_in_hex]
      properties:
        hash_mode:
          type: string
          enum: [Sha1, Sha224, Sha256, Sha384, Sha512, Sm3]
        key_in_hex:
          type: string
        message_in_hex:
          type: string

    # ── Symmetric Ciphers ────────────────────────────────────
    BlockCipherRequest:
      type: object
      required: [algorithm, operation, key_in_hex, iv_in_hex, data_in_base64]
      properties:
        algorithm:
          type: string
          enum: [AES128CBC, AES256CBC, SM4CBC]
        operation:
          type: string
          enum: [encrypt, decrypt]
        key_in_hex:
          type: string
          description: "Key hex (AES128=32chars, AES256=64chars, SM4=32chars)"
        iv_in_hex:
          type: string
          description: "IV hex (always 32 chars / 16 bytes)"
        data_in_base64:
          type: string

    StreamCipherRequest:
      type: object
      required: [algorithm, operation, key_in_hex, nonce_in_hex, data_in_base64]
      properties:
        algorithm:
          type: string
          enum: [AES256GCM, ChaCha20Poly1305, SM4GCM]
        operation:
          type: string
          enum: [encrypt, decrypt]
        key_in_hex:
          type: string
          description: "Key hex (AES256GCM/SM4GCM=32B, ChaCha20=32B)"
        nonce_in_hex:
          type: string
          description: "Nonce hex (GCM=24chars/12B, ChaCha20=24chars/12B)"
        data_in_base64:
          type: string
        aad_in_hex:
          type: string
          description: "Optional Additional Authenticated Data"

    ZucCipherRequest:
      type: object
      required: [key_in_hex, iv_in_hex, data_in_base64]
      properties:
        key_in_hex:
          type: string
          description: "128-bit key (32 hex chars)"
        iv_in_hex:
          type: string
          description: "128-bit IV (32 hex chars)"
        data_in_base64:
          type: string

    # ── RSA ──────────────────────────────────────────────────
    GenerateRsaRequest:
      type: object
      required: [key_size]
      properties:
        key_size:
          type: integer
          enum: [2048, 3072, 4096]
        password:
          type: string
          description: "Optional PEM encryption password"

    RsaEncryptionRequest:
      type: object
      required: [public_key_in_pem, padding_mode, plaintext_in_base64]
      properties:
        public_key_in_pem:
          type: string
        padding_mode:
          type: string
          enum: [RSAES_PKCS1_V1_5, RSAES_OAEP_SHA_256, RSAES_OAEP_SHA_384, RSAES_OAEP_SHA_512]
        plaintext_in_base64:
          type: string

    RsaDecryptionRequest:
      type: object
      required: [private_key_in_pem, padding_mode, ciphertext_in_base64]
      properties:
        private_key_in_pem:
          type: string
        padding_mode:
          type: string
          enum: [RSAES_PKCS1_V1_5, RSAES_OAEP_SHA_256, RSAES_OAEP_SHA_384, RSAES_OAEP_SHA_512]
        ciphertext_in_base64:
          type: string
        password:
          type: string
          description: "Private key PEM password (if encrypted)"

    RsaSignRequest:
      type: object
      required: [private_key_in_pem, sign_mode, message_in_base64]
      properties:
        private_key_in_pem:
          type: string
        sign_mode:
          type: string
          enum: [RSA_SIGN_PKCS1_V1_5_SHA256, RSA_SIGN_PKCS1_V1_5_SHA384, RSA_SIGN_PKCS1_V1_5_SHA512, RSA_SIGN_PSS_SHA256, RSA_SIGN_PSS_SHA384, RSA_SIGN_PSS_SHA512]
        message_in_base64:
          type: string
        password:
          type: string

    RsaVerifyRequest:
      type: object
      required: [public_key_in_pem, sign_mode, message_in_base64, signature_in_base64]
      properties:
        public_key_in_pem:
          type: string
        sign_mode:
          type: string
          enum: [RSA_SIGN_PKCS1_V1_5_SHA256, RSA_SIGN_PKCS1_V1_5_SHA384, RSA_SIGN_PKCS1_V1_5_SHA512, RSA_SIGN_PSS_SHA256, RSA_SIGN_PSS_SHA384, RSA_SIGN_PSS_SHA512]
        message_in_base64:
          type: string
        signature_in_base64:
          type: string

    # ── ECC ──────────────────────────────────────────────────
    GenerateEccRequest:
      type: object
      required: [algorithm]
      properties:
        algorithm:
          type: string
          enum: [secp256r1, secp384r1, secp521r1, secp256k1, Ed25519, X25519]
        password:
          type: string

    EccKeyExchangeRequest:
      type: object
      required: [client_private_key_in_pem, server_public_key_in_pem, key_length]
      properties:
        client_private_key_in_pem:
          type: string
        server_public_key_in_pem:
          type: string
        key_length:
          type: integer
          enum: [16, 24, 32, 48, 64]
          description: "Derived key length in bytes"
        client_password:
          type: string
        info_in_hex:
          type: string
          description: "HKDF info parameter (hex)"
        salt_in_hex:
          type: string
          description: "HKDF salt parameter (hex)"

    EccSignRequest:
      type: object
      required: [private_key_in_pem, hash_algorithm, message_in_base64]
      properties:
        private_key_in_pem:
          type: string
        hash_algorithm:
          type: string
          enum: [SHA256, SHA384, SHA512]
        message_in_base64:
          type: string
        password:
          type: string

    EccVerifyRequest:
      type: object
      required: [public_key_in_pem, hash_algorithm, message_in_base64, signature_in_base64]
      properties:
        public_key_in_pem:
          type: string
        hash_algorithm:
          type: string
          enum: [SHA256, SHA384, SHA512]
        message_in_base64:
          type: string
        signature_in_base64:
          type: string

    # ── SM2 ──────────────────────────────────────────────────
    GenerateSm2Request:
      type: object
      properties:
        password:
          type: string

    Sm2EncryptionRequest:
      type: object
      required: [public_key_in_pem, plaintext_in_base64, cipher_format]
      properties:
        public_key_in_pem:
          type: string
        plaintext_in_base64:
          type: string
        cipher_format:
          type: string
          enum: [C1C3C2_ASN1, C1C2C3_ASN1, C1C3C2, C1C2C3]

    Sm2DecryptionRequest:
      type: object
      required: [private_key_in_pem, ciphertext_in_base64, cipher_format]
      properties:
        private_key_in_pem:
          type: string
        ciphertext_in_base64:
          type: string
        cipher_format:
          type: string
          enum: [C1C3C2_ASN1, C1C2C3_ASN1, C1C3C2, C1C2C3]
        password:
          type: string

    Sm2SignRequest:
      type: object
      required: [private_key_in_pem, message_in_base64, signature_format]
      properties:
        private_key_in_pem:
          type: string
        message_in_base64:
          type: string
        signature_format:
          type: string
          enum: [RS_ASN1, RS_RAW]
        user_id_in_hex:
          type: string
          description: "SM2 user ID (default: '1234567812345678' hex)"
        password:
          type: string

    Sm2VerifyRequest:
      type: object
      required: [public_key_in_pem, message_in_base64, signature_in_base64, signature_format]
      properties:
        public_key_in_pem:
          type: string
        message_in_base64:
          type: string
        signature_in_base64:
          type: string
        signature_format:
          type: string
          enum: [RS_ASN1, RS_RAW]
        user_id_in_hex:
          type: string

    # ── PQC: ML-KEM ──────────────────────────────────────────
    MlKemKeygenRequest:
      type: object
      required: [algorithm]
      properties:
        algorithm:
          type: string
          enum: [ML-KEM-512, ML-KEM-768, ML-KEM-1024]
        seed_in_hex:
          type: string
          description: "Optional 64-byte seed (128 hex chars). If omitted, random seed is used."
        use_random_seed:
          type: boolean

    MlKemEncapRequest:
      type: object
      required: [algorithm, public_key_in_hex]
      properties:
        algorithm:
          type: string
          enum: [ML-KEM-512, ML-KEM-768, ML-KEM-1024]
        public_key_in_hex:
          type: string
          description: "Encapsulation key (512=1600chars, 768=2368chars, 1024=3136chars)"

    MlKemDecapRequest:
      type: object
      required: [algorithm, secret_key_in_hex, ciphertext_in_hex]
      properties:
        algorithm:
          type: string
          enum: [ML-KEM-512, ML-KEM-768, ML-KEM-1024]
        secret_key_in_hex:
          type: string
        ciphertext_in_hex:
          type: string

    HybridKexRequest:
      type: object
      required: [client_x25519_private_key_pem, client_x25519_public_key_pem, server_x25519_private_key_pem, server_x25519_public_key_pem, ml_kem_public_key_in_hex, ml_kem_secret_key_in_hex]
      properties:
        client_x25519_private_key_pem:
          type: string
        client_x25519_public_key_pem:
          type: string
        server_x25519_private_key_pem:
          type: string
        server_x25519_public_key_pem:
          type: string
        ml_kem_public_key_in_hex:
          type: string
        ml_kem_secret_key_in_hex:
          type: string
        ml_kem_algorithm:
          type: string
          enum: [ML-KEM-512, ML-KEM-768, ML-KEM-1024]
          default: ML-KEM-768

    # ── PQC: ML-DSA ──────────────────────────────────────────
    MlDsaKeygenRequest:
      type: object
      required: [algorithm]
      properties:
        algorithm:
          type: string
          enum: [ML-DSA-44, ML-DSA-65, ML-DSA-87]

    MlDsaSignRequest:
      type: object
      required: [algorithm, private_key_in_hex, message_in_hex]
      properties:
        algorithm:
          type: string
          enum: [ML-DSA-44, ML-DSA-65, ML-DSA-87]
        private_key_in_hex:
          type: string
          description: "PKCS#8 DER encoded private key (hex)"
        message_in_hex:
          type: string
          description: "Message to sign (hex, max 256 bytes)"

    MlDsaVerifyRequest:
      type: object
      required: [algorithm, public_key_spki_in_hex, message_in_hex, signature_in_hex]
      properties:
        algorithm:
          type: string
          enum: [ML-DSA-44, ML-DSA-65, ML-DSA-87]
        public_key_spki_in_hex:
          type: string
          description: "SPKI DER encoded public key (hex)"
        message_in_hex:
          type: string
        signature_in_hex:
          type: string

    # ── Utility ──────────────────────────────────────────────
    GenerateRandomDataRequest:
      type: object
      required: [length]
      properties:
        length:
          type: integer
          minimum: 1
          maximum: 128
          description: "Number of random bytes to generate"

    DataPaddingRequest:
      type: object
      required: [operation, data_in_hex, block_size]
      properties:
        operation:
          type: string
          enum: [pad, unpad]
        data_in_hex:
          type: string
        block_size:
          type: integer
          enum: [8, 16]
