This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Application Protector C

Learn about the Application Protector (AP) C.

Protegrity Application Protector (AP) C Overview

The Protegrity Application Protector (AP) C provides APIs that integrate with customer applications to protect, unprotect, and reprotect sensitive data. The AP C can be used with any customer application that is developed using the C programming language.

The AP C has the following protection and security access methods:

  • Get product version
  • Check access rights for the user
  • Protect
  • Unprotect
  • Reprotect

Features of AP C

The following are the various features of AP C.

Supported C standards and compilers

The AP C supports GCC versions 9.3.1 and higher for all operating systems.

Trusted applications

The AP C can be accessed only by trusted applications. Any application that protects, unprotects, or reprotects data must first be created as a trusted application.

For more information about how to make an application trusted, refer to Creating a Trusted Application.

Session validity

A session is valid until the XCTerminateLib API is called in the application.

Note: XCInitLib, XCOpenSession, XCCloseSession, and XCTerminateLib must each be called once per single process.

Audit logs

  • Each session generates audit records for every protection function call and data element.

  • Single Data Item Operations

    • Examples:
      • 1 protect operation with data element a and count 1 → 1 audit log
      • 5 protect operations with data element b and count 5 → 5 audit logs
      • 1000 unprotect operations with data element a and count 1000 → 1000 audit logs
  • Bulk Data Item Operations

    • Every protect, unprotect, and reprotect operation generates audit log events.
    • Example: 2 bulk protect operations with bulk size 3, the same data elements, and the same session → 1 audit log with count 6
  • Initialization Logs

    • An audit log is generated each time a trusted application is initialized, indicating whether the initialization was successful or not.
    • Initialization audit logs are created only after the xcpep.plm file is loaded.
    • Audits are generated in ESA Audit Store for Security Officer access.

Protector status audit logs

While the protector is running, a status log is sent to Discover at an interval of 60 seconds, which can be viewed using the pty_insight_analytics\*protector_status_* index on Discover.
For more information about the status logs, refer to Protectors status index.

The protector status dashboard displays the protector connectivity status through a pie chart and a table visualization. This dashboard uses status logs sent by the protector, so the protector which performed at least one security operation shows up on this dashboard.
For more information about the protector status dashboard, refer to Protector Status Dashboard index.

Error handling

If AP C is used to perform a security operation on a single data item, then the function returns a non-zero error code in case of any error. Similarly, if AP C is used to perform a bulk security operation, then a non-zero error code is returned for each failed item in the bulk result array. The calling application must check the return code after every AP C function call.

For more information about the log return codes, refer to Log return codes.

1 - Understanding the Architecture

The architecture and workflow of Application Protector.

This page describes the architecture, the individual components, and the workflow of the Protegrity Application Protector (AP) solution.

Architecture and Workflow

The following figure illustrates the deployment architecture of the Application Protector (AP).

Architecture and Workflow of Application Protector

The following table describes the components of the AP deployment architecture.

ComponentDescription
Customer ApplicationBuilt in supported programming languages and integrates with AP for data protection.
Application ProtectorCore protection engine that enforces security policies and performs data protection operations.
Configuration File (config.ini)Contains initialization parameters passed to AP during startup.
Package Enforcement and DeploymentDownloads policy packages from the RP Agent and executes protection operations, such as, protect, unprotect, and reprotect.
Log ForwarderCollects logs from AP and forwards them to the Audit Store for centralized auditing.
Resilient Package (RP) AgentStandalone process that retrieves policy packages from ESA and shares them with AP processes using shared memory IPC.

The following steps describe the workflow of a sample AP deployment in the production environment.

  1. The customer application initializes the SDK.

  2. The configurations that are required configuration parameters are passed to the protector using the config.ini file.

    Note: The configurations can be set through environment variables. ENV overrides values in the config.ini file, except for cadence and session timeout which must be set in the config file.

    For more information about environment variables configuration, refer to:

  3. The RP Agent regularly syncs with the RP Proxy or ESA to check for policy updates. If a change is detected, the updated policy package is securely downloaded over a TLS channel and stored in shared memory.

  4. The protector synchronizes with shared memory based on the cadence value defined in config.ini file. If a new package is available, it is fetched into process memory. This updated package is then used to perform data protection operations such as, protect, unprotect, and reprotect.

  5. The audit logs generated during protection operations are forwarded to the Audit Store:

    • Logs from the application are sent through the Log Forwarder
    • Logs from the RP Agent are also forwarded using the Log Forwarder

Components of the Application Protector

The Protegrity Application Protector (AP) solution comprises several key components that work together to enforce data protection policies and ensure secure operations.

Application Protector

The core engine that integrates with customer applications to perform data protection operations:

  • Protect
  • Unprotect
  • Reprotect

AP is available in multiple language-specific variants. One of which is:

  • AP C: For applications developed in C language

Resilient Package (RP) Agent

A standalone process responsible for policy synchronization:

  • To sync with the RP Proxy or ESA at regular intervals of 60 seconds
  • To detect policy changes and download updated packages over a secure TLS channel
  • To store the packages in shared memory for use by the protector

Log Forwarder

A log processing tool that handles audit and protection logs:

  • Collects logs generated by AP and RP Agent
  • Forwards logs to the Audit Store within ESA

Ports used to transport the protection and audit logs to the ESA:

  • 15780: Configurable
  • 15781: Non-configurable

Package Deployment

The different approaches for package deployment during the initialization process of the Application Protector are described in this section.

Dynamic Package Deployment

Use this approach when the protector needs to continuously check for policy updates after initialization.

  • The cadence parameter accepts values ranging from 0 to 86400.
  • This value defines the interval in seconds at which the protector synchronizes with the RP Agent.
  • If a policy change is detected, the protector automatically fetches the updated package and applies it during protection operations.

    Note: This method ensures that the protector always operates with the latest policy.

Immutable Package Deployment

Use this approach when the protector does not need to check for policy changes after initialization.

  • Set the cadence parameter to zero in the config.ini file for Immutable Package Deployment.
  • Add the [devops] parameter in the config.ini file before initializing the protector.
  • A REST API call is used to download an envelope-encrypted package from the ESA.
  • The protector uses this static package for all operations without further synchronization.
    For more information about the DevOps approach, refer to DevOps Approach for Application Protector.

2 - System Requirements

Lists the recommended minimum system requirements

The following table lists the minimum hardware configurations.

Hardware ComponentConfiguration Details
CPUDepends on the application.
Disk SpaceUnder 200 MB - including LogForwarder, RP Agent, and AP C.
RAMMemory usage depends on the AP flavor and application behavior.
Refer to AP C.

3 - Preparing the Environment

Prerequisites for AP C Installation.

Preparing the Environment for AP C Installation

Before installing Protegrity Application Protector (AP) C on a Linux platform, ensure the following prerequisites are met:

Prerequisites

  • The Enterprise Security Administrator (ESA) is installed, configured, and running.
  • The IP address or host name of the Load Balancer, Proxy, or ESA is noted.
  • The Policy Management (PIM) is initialized on the ESA, creating cryptographic keys and the policy repository for data protection.
    For more information about initializing the PIM, refer to Initializing the Policy Management.
  • A trusted application is created and configured in the ESA for the application that will use AP C. For more information, refer to Creating a Trusted Application.

4 - Installing the AP C Protector

Steps to setup AP C

Extracting the Setup Scripts and Package

To extract the setup scripts and package:

  1. Download the ApplicationProtector_Linux-ALL-64_x86-64_GCC-9.3.1_<version>.tgz file to any location on the machine where you want to install the protector.
  2. Extract the AP C installation package using the following command.
    tar –xvf ApplicationProtector_Linux-ALL-64_x86-64_GCC-9.3.1_<version>.tgz
    
    The following setup files are extracted:
    • ApplicationProtector_Linux-ALL-64_x86-64_GCC-9.3.1_<version>.tgz
    • signatures/ApplicationProtector_Linux-ALL-64_x86-64_GCC-9.3.1_<version>.sig
  3. Verify the digital signature of the signed AP C build.
    For more information about verifying the signed AP C build, refer to Verification of Signed Protector Build.
  4. Extract the AP C installation package again using the following command.
    tar –xvf ApplicationProtector_Linux-ALL-64_x86-64_GCC-9.3.1_<version>.tgz
    
    The following setup files are extracted:
    • LogforwarderSetup_Linux_x64_<version>.sh
    • RPAgentSetup_Linux_x64_<version>.sh
    • XCDevSetup_Linux_x64_<version>.sh
    • XCSamplesSetup_Linux_x64_<version>.sh

Installing Log Forwarder on Linux

The steps to install the Log Forwarder on a Linux platform using the Interactive mode or through the Silent mode are described in this section.

Note: To preserve all the configurations while upgrading the Log Forwarder, ensure that you backup all the files present under the /opt/protegrity/logforwarder/data/config.d directory.

For more information about installing Log Forwarder on Linux, refer to Installing Log Forwarder on Linux.

Using Interactive Mode

For more information about installing the Log Forwarder using Interactive Mode, refer to Installing Log Forwarder on Linux using Interactive Mode.

Using Silent Mode

For more information about installing Log Forwarder using the silent mode, refer to Installing Log Forwarder on Linux using Silent Mode.

Installing RP Agent on Linux

The steps to install the RP Agent on a Linux platform using the Interactive mode or through the Silent mode of installation are described in this section.

For more information about installing RP Agent, refer to Installing RP Agent on Linux or Unix.

Using Interactive Mode

For more information about installing RP Agent on Linux using Interactive Mode, refer to Installing RP Agent on Linux or Unix using Interactive Mode.

Using Silent Mode

For more information about installing RP Agent using Silent Mode, refer to Installing RP Agent on Linux or Unix using Silent Mode.

Installing Application Protector C on Linux

The steps to install the AP C on a Linux platform using the Linux installer or through the Silent mode of installation, are described in this section.

Using Linux Installer

To install the AP C on the Linux platform using the Linux installer:

  1. Run the AP C installer using the following command.

    ./XCDevSetup_Linux_x64_<version>.sh
    

    The prompt to continue the installation appears.

    *****************************************************
    Welcome to the C SDK Setup Wizard
    *****************************************************
    
    This will install the XCDev on your computer.
    Do you want to continue? [yes or no]
    
  2. If you want to continue with the installation of the XCDev, then type yes else type no.

    If you type yes, then the prompt to enter the installation directory appears.

    Enter installation directory.
    A new directory will be created in the installation directory.
    [/opt/protegrity]:
    

    If you type no, then the installation of the XCDev aborts.

    Note:
    The xcpep.plm file is installed in the /opt/protegrity/sdk/c/bin directory.

    The xcpep.plm file is a shared library that is required for compiling the applications which use AP C APIs. Link your application to the xcpep.plm file.

    The default installation directory for the XCDev on a Linux platform is /opt/protegrity/sdk/c.

  3. Run the AP C sample installer using the following command.

    ./XCSamplesSetup_Linux_x64_<version>.sh
    

    The prompt to continue the installation appears.

    *****************************************************
    Welcome to the C SDK Samples Setup Wizard
    *****************************************************
    
    This will install XCSamples on your computer.
    Do you want to continue? [yes or no]
    
  4. If you want to continue with the installation of the XCSamples, then type yes else type no.

    If you type yes, then the prompt to enter the installation directory appears.

    Enter installation directory.
    A new directory will be created in the installation directory.
    [/opt/protegrity]:
    

    The XCSamples are installed in /opt/protegrity/sdk/c by default.

    If you type no, then the installation of the XCSamples aborts.

    The following samples are installed in the /opt/protegrity/sdk/c/samples/ directory:

    • xcapcsample/
    • xccsharpsample/
    • Sample codes support Token DEs only.
    • To use FPE and Unicode Gen2 DEs, user needs to make use of ui4CharSet field in XC_PARAM_EX. For more information about ui4CharSet field, refer to values supported for ui4CharSet field.

    For more information about the samples, refer to the readme.txt file located in the respective sample directories.

    Note:

    • For C# (.NET) development, Protegrity recommends using the Application Protector for .NET.

    The AP C is successfully installed.

Using Silent Mode

You can also execute the AP C installer without any manual intervention, which is also known as the Silent mode of installation. The following parameter must be provided to execute the installer in the Silent mode.

ParameterDescription
-dirOptional install directory
Default: /opt/protegrity
  1. Run the AP C installer using the following command.
./XCDevSetup_Linux_x64_<version>.sh [-dir <directory>]

The following output appears.

XCDev installed in `/opt/protegrity/sdk/c`.
  1. Run the AP C sample installer using the following command.
./XCSamplesSetup_Linux_x64_<version>.sh [-dir <directory>]

The following output appears.

XCSamples installed in `/opt/protegrity/sdk/c`.

5 - Configuring the Protector

Configuring and Verifying AP C Installation on different platforms

Configuring AP C on Linux

To configure the AP C on the Linux platform:

  1. Set the library path before running protect, unprotect or reprotect operations.

    export LD_LIBRARY_PATH=/opt/protegrity/sdk/c/bin
    

    This makes the xcpep.plm file accessible to the Application Protector C SDK before your application initializes.

  2. Ensure that the config.ini file exists in this location before starting the application. AP C resolves the config.ini file path relative to the library path:

    • AP C automatically looks for the configuration file at -

      <installation_dir>/sdk/c/data/config.ini
      
    • To use a custom location, set the PTY_APC_CONFIG environment variable to the full path of your config.ini file:

      export PTY_APC_CONFIG=/path/to/config.ini
      

For more information about configuring the various parameters for the AP C using the config.ini file, refer to Config.ini file for Application Protector.

Verifying Installation of AP C on Linux

To verify the successful installation of the AP C:

  1. Deploy a policy in the ESA data store.

    For more information about deploying a policy, refer to Deploying Policies.

  2. Configure the application as a trusted application in the ESA.
    For more information about trusted applications, refer to Working With Trusted Applications.

    For more information about the sample application, refer to samples installed in directory.

6 - Using the AP C APIs

Sample application for the AP C.

The process to use the AP C protect, unprotect, and reprotect methods are described on this page.

It is assumed that the ESA is already available.

The tasks can be divided in the following order.

  1. Create the data elements and data store in the Policy Management on the ESA Web UI.
  2. Create the member sources and roles.
  3. Configure the policy.
  4. Configure the trusted application.
  5. Add a trusted application to the data store.
  6. Install the AP C.
  7. Run the sample application.

Creating a data element and data store

Determine how the data needs to be protected either by using encryption or tokenization before running the application. Protection and unprotection methods are available for both.

Create a data element and data store in the ESA by performing the following.

  1. To create a data element, from the ESA Web UI, navigate to Policy ManagementData Elements & MasksData Elements.
    For more information about creating data elements, refer to Working With Data Elements.
  2. To create a data store, navigate to Policy ManagementData Stores.
    For more information about creating data stores, refer to Creating a Data Store.

Creating a member source and role

Create a member source and role in the ESA by performing the following.

  1. To create a member source, from the ESA Web UI, navigate to Policy ManagementRoles & Member SourcesMember Sources.
    For more information about creating a member source, refer to Working With Member Sources.
  2. To create a role, from the ESA Web UI, navigate to Policy ManagementRoles & Member SourcesRoles.
    For more information about creating a role, refer to Working with Roles.

Configuring a policy

Configure a policy in the ESA by performing the following.

  1. From the ESA Web UI, navigate to Policy ManagementPolicies & Trusted ApplicationsPolicies.
  2. Click Add New Policy.
    The New Policy screen appears.
  3. After the policy is configured for the application user, add the permissions, data elements, roles, and data stores to the policy and then save it.
  4. Deploy the policy using the Policy Management Web UI.

For more information about creating a data security policy, refer to Creating Policies.

Configuring a trusted application

Only the applications and users configured as trusted applications under the ESA security policy can access the AP APIs.
If a policy is deployed but the application or the user is not trusted, then the AP aborts with the following message while performing the protect or unprotect operations.
API consumer is not part of the trusted applications, please contact the Security Officer

Configure a trusted application in the ESA by performing the following.

  1. From the ESA Web UI, navigate to Policy ManagementPolicies & Trusted ApplicationsTrusted Application.
  2. Create a trusted application.
  3. Deploy the trusted application using the Policy Management Web UI.

For more information about trusted applications, refer to Working With Trusted Applications.

Adding a trusted application to data store

Add a trusted application to data store by performing the following.

  1. From the ESA Web UI, navigate to Policy ManagementData Stores.
    The list of all the data stores appear.
  2. Select the required data store.
    The screen to edit the data store appears.
  3. Under the Trusted Applications tab, click Add.
    The screen to add the trusted application appears.
  4. Select the required trusted application and click Add.
  5. Select the required policy and deploy it using the Policy Management Web UI.

For more information about adding a trusted application to data store, refer to Linking Data Store to a Trusted Application.

Installing the AP C

Install the AP C by performing the following.

  1. To install the AP C, refer to Application Protector C Installation.

  2. Verify if the AP C is installed successfully. For more information about verifying the AP C installation, refer to Verifying the AP C Installation.

Running the AP C APIs

After setting up the policy and trusted application, you can begin testing the AP C APIs for protection, unprotection, and reprotection.

For more information about the AP C APIs, refer to Application Protector C APIs.

For more information about the AP C return codes, refer to Application Protector API Return Codes.

To run this sample application, ensure that the Application Name in the Trusted Application is set as HelloWorld.

For more information about the sample application, refer to samples installed in directory.

7 - Application Protector C APIs

The various APIs of the AP C.

The Protegrity Application Protector (AP) C provides APIs that integrate with the customer application to protect, unprotect, and reprotect sensitive data. A session must be created to run the AP C.

Note:

  • The AP C APIs can be invoked by a valid Policy User.
  • The AP C supports only the byte data type.

The following diagram represents the basic flow of a session.

AP C APIs

The following sections provide detailed information of the various structures and functions used by the Protegrity Application Protector C.

stXC_PARAM_EX

The stXC_PARAM_EX structure is used for the XC APIs that are used by the AP C. This structure contains metadata and information related to protect, unprotect, and reprotect operations.

The following is a sample of the stXC_PARAM_EX structure.

typedef struct stXC_PARAM_EX
{
  XC_CHAR   szVendor[XC_MAX_VENDOR_NAME_SIZE];          /**< It is for internal use, Do not use ! */
  XC_UINT8  ui8ScambledSessionHandle;                   /**< It is for internal use, Do not use ! */
  XC_CHAR   cUserIp[XC_MAX_IP_ADDRESS_SIZE];            /**< It is for internal use, Do not use ! */
  XC_UINT4  ui4Operation;                               /**< It is for internal use, Do not use ! */
  XC_UINT4  ui4DataType;                                /**< It is for internal use, Do not use ! */
  XC_UINT4  ui4CharSet;                                 /**< Character set of the input data (UTF-8/UTF-16/UTF-32). Unknown values are treated as UTF-8. */
  XC_CHAR   cPlainTextEncoding[XC_ENCODING_SIZE];       /**< It is for internal use, Do not use ! */
  XC_BYTE   bOldExternalIV[XC_MAX_IV_SIZE];             /**< It is for internal use, Do not use ! */
  XC_UINT4  ui4OldExternalIVLength;                     /**< It is for internal use, Do not use ! */
  XC_BYTE   bNewExternalIV[XC_MAX_IV_SIZE];             /**< It is for internal use, Do not use ! */
  XC_UINT4  ui4NewExternalIVLength;                     /**< It is for internal use, Do not use ! */
  XC_BYTE   bOldExternalTweak[XC_MAX_TWEAK_SIZE];       /**< Old external tweak for FPE. Used by unprotect and reprotect operations. */
  XC_UINT4  ui4OldExternalTweakLength;                  /**< Length in bytes of bOldExternalTweak. Set to 0 when no tweak is used. */
  XC_BYTE   bNewExternalTweak[XC_MAX_TWEAK_SIZE];       /**< New external tweak for FPE. Used by protect and reprotect operations. */
  XC_UINT4  ui4NewExternalTweakLength;                  /**< Length in bytes of bNewExternalTweak. Set to 0 when no tweak is used. */
} XC_PARAM_EX;

stXC_DATA_ITEM

Note: It is recommended to use stXC_DATA_ITEM_EX structure over stXC_DATA_ITEM structure.

The stXC_DATA_ITEM structure is used as a container to store the protected or unprotected data.

The following is a sample for the stXC_DATA_ITEM structure.

typedef struct stXC_DATA_ITEM
{
  XC_UINT4 ui4Capacity; /* Max number of bytes that can be stored in pvValue  */
  XC_UINT4 ui4Length;   /* Actual number of bytes stored in pvValue           */
  XC_VOID* pvValue;     /* The actual value of the data                       */
} XC_DATA_ITEM;

stXC_DATA_ITEM_EX

The stXC_DATA_ITEM_EX structure is typically used for bulk operations with the various APIs.

typedef struct stXC_DATA_ITEM_EX
{
  XC_UINT4 ui4Capacity;    /**< Max number of bytes that can be stored in pvValue  */
  XC_UINT4 ui4Length;      /**< Actual number of bytes stored in pvValue           */
  XC_VOID* pvValue;        /**< The actual value of the data                       */
  XC_BYTE  bNullIndicator; /**< Used to set if the data is null or not. XC_TRUE = Null. XC_FALSE = not null  */
  XC_UINT4 ui4ErrorCode;   /**< The errorcode for the returning data struct, if an error occured, otherwise it is PEP_LOG_PROTECT_SUCCESS or PEP_LOG_UNPROTECT_SUCCESS */ 
} XC_DATA_ITEM_EX;

stXC_ACTION_RESULT

For each bulk call, the stXC_ACTION_RESULT structure contains a summary status for the entire batch.

The following is a sample for the stXC_ACTION_RESULT structure.

typedef struct stXC_ACTION_RESULT
{
  XC_UINT4 ui4LogReturnCode; /* This is the returncode of the operation */
  XC_UINT4 ui4LogSeverity;   /* This is the severity,  SUCCESS, SUCCESS WITH WARNING or ERROR */
  XC_UINT4 ui4NoAccessOperation; /* What should we return if we do not have access to unprotect data, NULL, EXCEPTION, PROTECTED VALUE or NOACCESS VALUE */
  XC_UINT4 ui4ProtectionAlgId; /* Algorithm that was used for protecting/unprotecting */
  XC_UINT4 ui4TokenType;       /* Type of data being tokenized */
  XC_UINT4 ui4OutputEncoding;  /* Encoding of output data */
  XC_MASK_SETTINGS stMaskSettings; /* Indicates type of masking used */
} stXC_ACTION_RESULT;

The following parameters are important for the bulk calls:

  • ui4LogReturnCode contains only the most severe error code in the processed batch (for example, if there is one success with warning entry and one with severe error, the severe error entry will be logged).

  • ui4LogSeverity reports on the status of the processed batch.

    The ui4LogSeverity has three different types. The first type applies to successful operations. For erroneous operations, there are two different states (success with warning and error) pointing to what went wrong in a batch.

The following table describes the various ui4LogSeverity types.

ui4LogSeverity Typeui4LogSeverity Possible Errors
Success (operation completed successfully)Operation completed successfully - no exception
Success With Warning (operation completed successfully but some data failed)Policy constraints:
- User/DE/Time/Access
- Load Key (Key ID)
- Audit failed
Data constraints:
- Integrity Check failed
- Invalid format
- Length
- Token alphabet constraints
Error
(Major error- batch failed)
System constraints:
- RPA not running
- Fatal errors
Exceptions:
- Policy locked
- Policy not available
- Unsupported algorithm
- Input Parameter missing
- Disk full
- No access operation = exception (Policy set)
- Out of memory

By turn, each data item in a batch contains its own log return code. It is defined by ui4ErrorCode in the XC_DATA_ITEM_EX structure for the AP C API.

stXC_MASK_SETTINGS

The stXC_MASK_SETTINGS structure holds information about the mask settings applied on the data element for access and audit purposes.

The following is a sample for the stXC_MASK_SETTINGS structure.

typedef struct stXC_MASK_SETTINGS
{
  XC_UINT4 bLeftIsMasked  : 1;
  XC_UINT4 bLeft          : 7;
  XC_UINT4 bRightIsMasked : 1;
  XC_UINT4 bRight         : 7;
  XC_UINT4 bFillerBits    : 16;
  XC_BYTE bMaskCharacter;
} XC_MASK_SETTINGS;

eXC_LOGRETURNTYPE

The eXC_LOGRETURNTYPE enum indicates the type of the log return code depending on the value returned.

The following is a sample for the eXC_LOGRETURNTYPE enum.

typedef enum eXC_LOGRETURNTYPE
{
  XC_LOGRETURNSUCCESS        = 0,  /* Success, no additional test            */
  XC_LOGRETURNSUCCESSWARNING = 1,  /* Success, with additional warning text  */
  XC_LOGRETURNERROR          = 2,  /* Error type of logreturn code           */
  XC_LOGRETURNEXCEPTION      = 3   /* If we want to throw exception if no acceess */
} XC_LOGRETURNTYPE;

eXC_FUNCTION

The eXC_FUNCTION enum indicates the type of algorithm used while protecting the data.

The following is a sample for the eXC_FUNCTION enum.

typedef enum eXC_FUNCTION
{
  XC_ANY_FUNCTION = 0,              /* This is the default - some kind of cipher function */
  XC_HMAC_FUNCTION = 1,             /* Hash message digest - one way cipher function      */
  XC_TYPE_PRESERVING_FUNCTION = 2,  /* Type Preserving, i.e. Token, NoEncryption         */
  XC_CRYPTO_FUNCTION = 3            /* Regular encryption algorithm           */
} XC_FUNCTION;

eXC_DATATYPE

The eXC_DATATYPE enum indicates the type of datatype you want to protect.

The following is a sample for the eXC_DATATYPE enum.

typedef enum eXC_DATATYPE
{
  XC_DATATYPE_BYTE = 0,     /* Byte */
  XC_DATATYPE_CHARACTER,    /* Character data */
  XC_DATATYPE_UNICODE,      /* Unicode data */
  XC_DATATYPE_DATE,         /* Date data */
  XC_DATATYPE_INTEGER,      /* Integer data */
  XC_DATATYPE_OTHER         /* For example real, blob, clob, time, timestamp */
} XC_DATATYPE;

XCInitLib Function

The XCInitLib function initializes the Protegrity AP library. This should be performed once in each application that uses the Protegrity AP API. When the application terminates, it should call the corresponding terminate function.

This function returns XC_SUCCESS on success.

The following is a sample for the XCInitLib function.

XCInitLib( XC_HANDLE* phXCHandle,
           const XC_CHAR* pcParameter );

The following table lists the various parameters used for the XCInitLib function.

ParameterDescriptionData Type
phXCHandle[out] A handle for the library. It should be used for the other functions.XC_HANDLE*
pcParameter[in] NULL terminated string containing the parameter.
CAUTION: This parameter is not used.
const XC_CHAR*

XCTerminateLib Function

The XCTerminateLib function terminates the Protegrity AP library. This should be performed when the application terminates. No other functions should be invoked after invoking the XCTerminateLib function. It should always be invoked after a call to the XCInitLib function for cleanup purposes.

This function returns XC_SUCCESS on success.

The following is a sample of the XCTerminateLib function.

XCTerminateLib( XC_HANDLE* phXCHandle );

The following table lists the parameter used for the XCTerminateLib function.

ParameterDescriptionData Type
phXCHandle[in/out] A handle for the library that has been initialized.XC_HANDLE*

XCGetVersion Function

The XCGetVersion function obtains a null terminated version string for the Protegrity AP.

This function returns XC_SUCCESS on success.

The following is a sample for the XCGetVersion function.

XCGetVersion( XC_CHAR* pszVersion,
              const XC_UINT4 ui4VersionLength );

The following table lists the various parameters used for the XCGetVersion function.

ParameterDescriptionData Type
pszVersion[in/out] The null terminated version string is returned. The buffer needs to be allocated before it is sent to the function.XC_CHAR*
ui4VersionLength[in] The length of the buffer allocated for the version.const XC_UINT4

XCGetVersionEx Function

The XCGetVersionEx function obtains a null terminated version string for the Protegrity AP. It returns the current version of the AP C and the core version.

The following is a sample for the XCGetVersionEx function.

XCGetVersionEx();

XCGetCoreVersion Function

The XCGetCoreVersion function obtains a null terminated version string for the Protegrity AP.

This function returns XC_SUCCESS on success.

The following is a sample for the XCGetCoreVersion function.

XCGetCoreVersion( XC_CHAR* pszCoreVersion, const XC_UINT4 ui4CoreVerLen );

The following table lists the various parameters used for the XCGetCoreVersion function.

ParameterDescriptionData Type
pszCoreVersion[in/out] The null terminated version string is returned. The buffer needs to be allocated before it is sent to the function.XC_CHAR*
ui4CoreVerLen[in] The length of the buffer allocated for the version.const XC_UINT4

XCOpenSession Function

The XCOpenSession function opens a session and returns a handle for that session to be used in calls to the XCProtect and XCUnprotect functions. When the session is no longer needed, it should be closed by a call to XCCloseSession function.

This function returns XC_SUCCESS on success.

The following is a sample for the XCOpenSession function.

XCOpenSession( const XC_HANDLE    hXCHandle,
               const XC_CHAR*     pcUser,
               const XC_CHAR*     pcPassword,
               const XC_CHAR*     pcParameter,
               XC_SESSION*        phSession );

The following table lists the various parameters used for the XCOpenSession function.

ParameterDescriptionData Type
hXCHandle[in] A handle for the library that has been initialized.const XC_HANDLE
pcUser[in] NULL terminated string for the user opening the session.
Important: This parameter is only applicable for the AP Lite.
const XC_CHAR*
pcPassword[in] NULL terminated string for the password for that user.
Important: This parameter is only applicable for the AP Lite.
const XC_CHAR*
pcParameter[in] NULL terminated string containing the parameter needed to create a session.
For AP Client, the required parameter is ipaddress;port;TCP for the PEP server that is configured to handle the AP requests.
For AP Lite, the required parameter is the <export_keys_filename>.xml file, which is the key export file from REST API service.
For the XCPep.plm file, the parameter 0 needs to be set for the communicationid.
const XC_CHAR*
phSession[out] On success, this will point to the handle for the established session.XC_SESSION*

XCCloseSession Function

The XCCloseSession function will terminate an established session and reset the handle for it, after which no calls to XCProtect and XCUnprotect functions will work.

This function returns XC_SUCCESS on success.

The following is a sample for the XCCloseSession function.

XCCloseSession( const XC_HANDLE hXCHandle,
                XC_SESSION* phSession );

The following table lists the various parameters used for the XCCloseSession function.

ParameterDescriptionData Type
hXCHandle[in] Handle for the library that has been initialized.const XC_HANDLE
phSession[in/out] Session to close.XC_SESSION*

XCProtect Function

The XCProtect function will take the supplied plaintext and send a request to the Protegrity AP that will protect the data. It will then read the response from the Protegrity AP and return the cipher text to the caller.

This function returns XC_SUCCESS on success.

Note:

  • Data protected using encryption data elements with input as integers, long, or short data types and output as bytes is platform-specific. You cannot move such protected data between platforms having different endianness.

    For example, consider data protected using encryption data elements with input as integers and output as bytes. You cannot move such protected data from the AIX platform to the Linux or Windows platform, and vice-versa.

The ui4CharSet field in XC_PARAM_EX is required when using FPE or Unicode Gen2 data elements with the byte API. It must match the encoding of the input data. The following values are supported:

ValueEncoding
2UTF-8
4UTF-16LE
5UTF-16BE

Warning:
For Date and DateTime type of data elements, the XCProtect API returns an invalid input data error for certain input values. This occurs when the input value falls within the non-existent date range from 05-OCT-1582 to 14-OCT-1582 of the Gregorian Calendar.

For more information about the tokenization and de-tokenization of the cutover dates, refer to Datetime Tokenization for Cutover Dates of the Proleptic Gregorian Calendar.

The following is a sample of the XCProtect function.

XCProtect( const XC_HANDLE     hXCHandle,
           const XC_SESSION    hSession,
           const XC_UINT4      ui4EventType,
           const XC_CHAR*      pcPolicyUser,
           const XC_CHAR*      pcDataElement,
           const XC_BYTE*      pcExternalIV,
           const XC_UINT4      ui4ExternalIVLength,
           const XC_BYTE*      pcInputData,
           const XC_UINT4      ui4InputDataLength,
           const XC_BYTE       bNiInputData,
           XC_BYTE*            pcOutputData,
           XC_UINT4*           pui4OutputDataLength,
           XC_BYTE*            pbNiOutputData,
           const XC_PARAM_EX*  pXCParam,
           const XC_UINT4      ui4XCParamSize,
           stXC_ACTION_RESULT* pstActionResult );

The following table lists the various parameters used for the XCProtect function.

ParameterDescriptionData Type
hXCHandle[in] Handle for the library that has been initialized.const XC_HANDLE 
hSession[in] Handle for the established session.const XC_SESSION 
ui4EventType[in] Used to identify whether the call type of the function is First Call or Normal Call.
This parameter can be set to one of the following values:
- 1: Internally sets the XC event type to XC_EVENT_FIRST_CALL. This identifies the call type as First Call. If the call type is First Call, then data is written to the internal cache, which includes:
- SessionID
- RequestID
- DataElement
- UserName
- ProductID
- VendorID
- 0: Identifies the call type as Normal Call. If the call type is Normal Call, then the data is only read from the internal cache. Data is written to the internal cache only if it is different from the one that was written during the First Call.

Important: An entry is generated in the pepserver.log file only if any new data is written to the internal cache.

Note:
If logging is not enabled, but the XC_EVENT_FIRST_CALL parameter is set, then logs are not generated.
Similarly, if logging is enabled, but XC_EVENT_FIRST_CALL is not set, then logs are generated only if any new data is written to the cache.
const XC_UINT4
pcPolicyUser[in] NULL terminated string for the policy user.const XC_CHAR * 
pcDataElement[in] NULL terminated string for the data element.const XC_CHAR * 
pcExternalIV[in] Buffer containing data that will be used as external IV.const XC_BYTE*
ui4ExternalIVLength[in] Number of bytes provided in the pcExternalIv buffer. The total amount of data to be protected should not exceed 256 bytes.const XC_UINT4
pcInputData[in] Buffer containing the plain data to encrypt.const XC_BYTE * 
ui4InputDataLength[in] Number of bytes contained in pcInputData. The total amount of data to be protected should not exceed 2 GB.const XC_UINT4 
bNiInputData[in] Flag to indicate NULL input.const XC_BYTE
pcOutputData[out] Buffer that will hold the resulting encrypted data. Needs to be larger than the input buffer.XC_BYTE * 
pui4OutputDataLength[in/out] On input it should specify the max size of the output data buffer. When the function returns, it will contain the actual number of bytes stored in the output. This size needs to be big enough to hold the resulting data as well as the packed request. Consider having an output buffer that is XC_BYTES_OVERHEAD bytes larger than the input buffer for additional overhead.

If the value is set to zero as *pui4OutputDataLength = 0;, then the following value is returned.

*pui4OutputDataLength = ui4InputDataLength + XC_BYTES_OVERHEAD;
XC_UINT4 * 
pbNiOutputData[out] Flag indicating that the output is NULL.XC_BYTE * 
pXCParam[in] Additional information associated with the protection, refer to XC_Param_Ex function.const XC_PARAM_EX*
ui4XCParamSize[in] Size of the structure passed in the pXCParam parameter.const XC_UINT4
pstActionResult[out] Structure containing the result of the operation. For more information refer to the xcdefinitions.h file.stXC_ACTION_RESULT*

XCUnprotect Function

The XCUnprotect function will take the supplied ciphered data and send a request to the Protegrity AP that will unprotect the data. It will then read the response from the Protegrity AP and return the plain data to the caller.

This function returns XC_SUCCESS on success.

The ui4CharSet field in XC_PARAM_EX is required when using FPE or Unicode Gen2 data elements with the byte API. It must match the encoding of the input data. The following values are supported:

ValueEncoding
2UTF-8
4UTF-16LE
5UTF-16BE

The following is a sample of the XCUnprotect function.

XCUnprotect( const XC_HANDLE     hXCHandle,
             const XC_SESSION    hSession,
             const XC_UINT4      ui4EventType,
             const XC_CHAR*      pcPolicyUser,
             const XC_CHAR*      pcDataElement,
             const XC_BYTE*      pcExternalIV,
             const XC_UINT4      ui4ExternalIVLength,
             const XC_BYTE*      pcInputData,
             const XC_UINT4      ui4InputDataLength,
             const XC_BYTE       bNiInputData,
             XC_BYTE*            pcOutputData,
             XC_UINT4*           pui4OutputDataLength,
             XC_BYTE*            pbNiOutputData,
             const XC_PARAM_EX*  pXCParam,
             const XC_UINT4      ui4XCParamSize,
             stXC_ACTION_RESULT* pstActionResult );

The following table lists the various parameters used for the XCUnprotect function.

ParameterDescriptionData Type
hXCHandle[in] Handle for the library that has been initialized.const XC_HANDLE
hSession[in] Handle for the established session.const XC_SESSION
ui4EventType[in] Used to identify whether the call type of the function is First Call or Normal Call.
This parameter can be set to one of the following values:
- 1: Internally sets the XC event type to XC_EVENT_FIRST_CALL. This identifies the call type as First Call. If the call type is First Call, then data is written to the internal cache, which includes:
- SessionID
- RequestID
- DataElement
- UserName
- ProductID
- VendorID
- 0: Identifies the call type as Normal Call. If the call type is Normal Call, then the data is only read from the internal cache. Data is written to the internal cache only if it is different from the one that was written during the First Call.

Important: An entry is generated in the pepserver.log file only if any new data is written to the internal cache.

Note:
If logging is not enabled, but the XC_EVENT_FIRST_CALL parameter is set, then logs are not generated.
Similarly, if logging is enabled, but XC_EVENT_FIRST_CALL is not set, then logs are generated only if any new data is written to the cache.
const XC_UINT4
pcPolicyUser[in] NULL terminated string for the policy user.const XC_CHAR *
pcDataElement[in] NULL terminated string for the data element.const XC_CHAR*
pcExternalIV[in] Buffer containing data that will be used as external IV.const XC_BYTE*
ui4ExternalIVLength[in] Number of bytes provided in the pcExternalIv buffer. The total amount of data to be unprotected should not exceed 256 bytes.const XC_UINT4
pcInputData[in] Buffer containing the data to unprotect.const XC_BYTE*
ui4InputDataLength[in] Number of bytes contained in pcInputData. The total amount of data to be unprotected should not exceed 2 GB.const XC_UINT4
bNiInputData[in] Flag to indicate NULL input.const XC_BYTE
pcOutputData[out] Buffer that will hold the resulting decrypted data. Needs to be larger than the input buffer.XC_BYTE*
pui4OutputDataLength[in/out] On input it should specify the max size of the output data buffer. When the function returns, it will contain the actual number of bytes stored in the output. This size needs to be big enough to hold the resulting data as well as the packed request. Consider having an output buffer that is XC_BYTES_OVERHEAD bytes larger than the input buffer for additional overhead.
If the value is set to zero as *pui4OutputDataLength = 0;, then the following value is returned.
*pui4OutputDataLength = ui4InputDataLength + XC_BYTES_OVERHEAD;
XC_UINT4*
pbNiOutputData[out] Flag indicating that the output is NULL.XC_BYTE*
pXCParam[in] Additional information associated with the protection, refer to the XC_Param_Ex function.const XC_PARAM_EX*
ui4XCParamSize[in] Size of the structure passed in the pXCParam parameter.const XC_UINT4
pstActionResult[out] Structure containing the result of the operation. For more information refer to the xcdefinitions.h file.stXC_ACTION_RESULT*

XCReprotect Function

The XCReprotect function will take the supplied protected data and send a request to the Protegrity AP that will reprotect the data. It will then read the response from the Protegrity AP and return the cipher text to the caller.

This function returns XC_SUCCESS on success.

The following is a sample for the XCReprotect function.

XCReprotect( const XC_HANDLE     hXCHandle,
             const XC_SESSION    hSession,
             const XC_UINT4      ui4EventType,
             const XC_CHAR*      pcPolicyUser,
             const XC_CHAR*      pcOldDataElement,
             const XC_CHAR*      pcNewDataElement,
             const XC_BYTE*      pcOldExternalIV,
             const XC_UINT4      ui4OldExternalIVLength,
             const XC_BYTE*      pcNewExternalIV,
             const XC_UINT4      ui4NewExternalIVLength,
             const XC_BYTE*      pcInputData,
             const XC_UINT4      ui4InputDataLength,
             const XC_BYTE       bNiInputData,
             XC_BYTE*            pcOutputData,
             XC_UINT4*           pui4OutputDataLength,
             XC_BYTE*            pbNiOutputData,
             const XC_PARAM_EX*  pXCParam,
             const XC_UINT4      ui4XCParamSize,
             stXC_ACTION_RESULT* pstActionResult );

The following table lists the various parameters used for the XCReprotect function.

ParameterDescriptionData Type
hXCHandle[in] Handle for the library that has been initialized.const XC_HANDLE
hSession[in] Handle for the established session.const XC_SESSION
ui4EventType[in] Used to identify whether the call type of the function is First Call or Normal Call.
This parameter can be set to one of the following values:
- 1: Internally sets the XC event type to XC_EVENT_FIRST_CALL. This identifies the call type as First Call. If the call type is First Call, then data is written to the internal cache, which includes:
- SessionID
- RequestID
- DataElement
- UserName
- ProductID
- VendorID
- 0: Identifies the call type as Normal Call. If the call type is Normal Call, then the data is only read from the internal cache. Data is written to the internal cache only if it is different from the one that was written during the First Call.

Important: An entry is generated in the pepserver.log file only if any new data is written to the internal cache.

Note:
If logging is not enabled, but the XC_EVENT_FIRST_CALL parameter is set, then logs are not generated.
Similarly, if logging is enabled, but XC_EVENT_FIRST_CALL is not set, then logs are generated only if any new data is written to the cache.const XC_UINT4
pcPolicyUser[in] String for the policy user.const XC_CHAR*
pcOldDataElement[in] String for the old data element.const XC_CHAR*
pcNewDataElement[in] String for the new data element to be used.const XC_CHAR*
pcOldExternalIV[in] External IV that was used when data was protected.const XC_BYTE*
ui4OldExternalIVLength[in] Number of bytes in pcOldExternalIV. The total amount of data to be reprotected should not exceed 256 bytes.const XC_UINT4
pcNewExternalIV[in] External IV to use when data is protected with the new data element.const XC_BYTE*
ui4NewExternalIVLength[in] Number of bytes in pcNewExternalIV. The total amount of data to be reprotected should not exceed 256 bytes.const XC_UINT4
pcInputData[in] Buffer containing the data to be re-encrypted.const XC_BYTE*
ui4InputDataLength[in] Number of bytes contained in pcInputData. The total amount of data to be unprotected should not exceed 2 GB.const XC_UINT4
bNiInputData[in] Flag to indicate NULL input.const XC_BYTE
pcOutputData[out] Buffer that will hold the resulting re-encrypted data. Needs to be larger than the input buffer.XC_BYTE*
pui4OutputDataLength[in/out] On input it should specify the max size of the output data buffer. When the function returns, it will contain the actual number of bytes stored in the output. This size needs to be big enough to hold the resulting data as well as the packed request. Consider having an output buffer that is XC_BYTES_OVERHEAD bytes larger than the input buffer for additional overhead.

If the value is set to zero as *pui4OutputDataLength = 0;, then the following value is returned.

*pui4OutputDataLength = ui4InputDataLength + XC_BYTES_OVERHEAD;
XC_UINT4*
pbNiOutputData[out] Flag indicating that the output is NULL.XC_BYTE*
pXCParam[in] Additional information associated with the protection, refer to the XC_Param_Ex function.const XC_PARAM_EX* 
ui4XCParamSize[in] Size of the structure passed in the pXCParam parameter.const XC_UINT4
pstActionResult[out] Structure containing the result of the operation. For more information refer to the xcdefinitions.h file.stXC_ACTION_RESULT*

XCBulkProtect Function

The XCBulkProtect function protects the data in bulk. It will take a list of data items and send them in a single request to the Protegrity AP, which will then process them in a batch. It will then read the response from the Protegrity AP and return a list of data items containing the protected data.

This function returns XC_SUCCESS on success.

The ui4CharSet field in XC_PARAM_EX is required when using FPE or Unicode Gen2 data elements with the byte API. It must match the encoding of the input data. The following values are supported:

ValueEncoding
2UTF-8
4UTF-16LE
5UTF-16BE

Warning:

For Date and DateTime type of data elements, the XCBulkProtect API returns an invalid input data error for certain input values. This occurs when the input value falls within the non-existent date range from 05-OCT-1582 to 14-OCT-1582 of the Gregorian Calendar.

For more information about the tokenization and de-tokenization of the cutover dates, refer to Datetime Tokenization for Cutover Dates of the Proleptic Gregorian Calendar.

The following is a sample of the XCBulkProtect function.

XCBulkProtect( const XC_HANDLE        hXCHandle,
               const XC_SESSION       hSession,
               const XC_UINT4         ui4EventType,
               const XC_CHAR*         pcPolicyUser,
               const XC_CHAR*         pcDataElement,
               const XC_BYTE*         pcExternalIV,
               const XC_UINT4         ui4ExternalIVLength,
               const XC_DATA_ITEM_EX* pInDataItems,
               const XC_UINT4         ui4InDataItemCount,
               XC_DATA_ITEM_EX*       pOutDataItems,
               XC_UINT4*              pui4OutDataItemCount,
               XC_INT4*               pi4ErrorIndex,
               const XC_PARAM_EX*     pXCParam,
               const XC_UINT4         ui4XCParamSize,
               stXC_ACTION_RESULT*    pstActionResult );

The following table lists the various parameters used for the XCBulkProtect function.

ParameterDescriptionData Type
hXCHandle[in] Handle for the library that has been initialized.const XC_HANDLE
hSession[in] Handle for the established session.const XC_SESSION
ui4EventType[in] Used to identify whether the call type of the function is First Call or Normal Call.
This parameter can be set to one of the following values:
- 1: Internally sets the XC event type to XC_EVENT_FIRST_CALL. This identifies the call type as First Call. If the call type is First Call, then data is written to the internal cache, which includes:
- SessionID
- RequestID
- DataElement
- UserName
- ProductID
- VendorID
- 0: Identifies the call type as Normal Call. If the call type is Normal Call, then the data is only read from the internal cache. Data is written to the internal cache only if it is different from the one that was written during the First Call.

Important: An entry is generated in the pepserver.log file only if any new data is written to the internal cache.

Note: If logging is not enabled, but the XC_EVENT_FIRST_CALL parameter is set, then logs are not generated.
Similarly, if logging is enabled, but XC_EVENT_FIRST_CALL is not set, then logs are generated only if any new data is written to the cache.
const XC_UINT4
pcPolicyUser[in] NULL terminated string for the policy user.const XC_CHAR*
pcDataElement[in] NULL terminated string for the data element.const XC_CHAR*
pcExternalIV[in] Buffer containing data that will be used as external IV.const XC_BYTE*
ui4ExternalIVLength[in] Number of bytes provided in the pcExternalIv buffer. The total amount of data for bulk protection should not exceed 256 bytes.const XC_UINT4
pInDataItems[in] Buffer containing the protected data to unprotect.const XC_DATA_ITEM_EX*
ui4InDataItemCount[in] Number of items contained in pInDataItems. The maximum number of data items should not exceed 1000 elements.const XC_UINT4
pOutDataItems[out] Buffer containing the unprotected data after a successful operation.XC_DATA_ITEM_EX*
pui4OutDataItemCount[out] Resulting number of items placed in pOutDataItems.XC_UINT4*
pi4ErrorIndex[out] Zero-based index of first item that failed (if any), otherwise it will be set to XC_ERROR_INDEX_NONE.XC_INT4*
pXCParam[in] Additional parameters.const XC_PARAM_EX*
ui4XCParamSize[in] Size of the additional parameters.const XC_UINT4
pstActionResult[out] Structure containing the result of the operation. For more information refer to the xcdefinitions.h file.stXC_ACTION_RESULT*

XCBulkUnProtect Function

The XCBulkUnProtect function decrypts the data in bulk. It will take a list of data items and send them in a single request to the Protegrity AP, which will then process them in a batch. It will then read the response from the Protegrity AP and return a list of data items containing the plain data.

This function returns XC_SUCCESS on success.

The ui4CharSet field in XC_PARAM_EX is required when using FPE or Unicode Gen2 data elements with the byte API. It must match the encoding of the input data. The following values are supported:

ValueEncoding
2UTF-8
4UTF-16LE
5UTF-16BE

The following is a sample of the XCBulkUnProtect function.

XCBulkUnprotect( const XC_HANDLE          hXCHandle,
                 const XC_SESSION         hSession,
                 const XC_UINT4           ui4EventType,
                 const XC_CHAR*           pcPolicyUser,
                 const XC_CHAR*           pcDataElement,
                 const XC_BYTE*           pcExternalIV,
                 const XC_UINT4           ui4ExternalIVLength,
                 const XC_DATA_ITEM_EX*   pInDataItems,
                 const XC_UINT4           ui4InDataItemCount,
                 XC_DATA_ITEM_EX*         pOutDataItems,
                 XC_UINT4*                pui4OutDataItemCount,
                 XC_INT4*                 pi4ErrorIndex,
                 const XC_PARAM_EX*       pXCParam,
                 const XC_UINT4           ui4XCParamSize,
                 stXC_ACTION_RESULT*      pstActionResult );

The following table lists the various parameters used for the XCBulkUnProtect function.

ParameterDescriptionData Type
hXCHandle[in] Handle for the library that has been initialized.const XC_HANDLE
hSession[in] Handle for the established session.const XC_SESSION
ui4EventType[in] Used to identify whether the call type of the function is First Call or Normal Call.
This parameter can be set to one of the following values:
- 1: Internally sets the XC event type to XC_EVENT_FIRST_CALL. This identifies the call type as First Call. If the call type is First Call, then data is written to the internal cache, which includes:
- SessionID
- RequestID
- DataElement
- UserName
- ProductID
- VendorID
- 0: Identifies the call type as Normal Call. If the call type is Normal Call, then the data is only read from the internal cache. Data is written to the internal cache only if it is different from the one that was written during the First Call.

Important: An entry is generated in the pepserver.log file only if any new data is written to the internal cache.

Note: If logging is not enabled, but the XC_EVENT_FIRST_CALL parameter is set, then logs are not generated.
Similarly, if logging is enabled, but XC_EVENT_FIRST_CALL is not set, then logs are generated only if any new data is written to the cache.
const XC_UINT4
pcPolicyUser[in] NULL terminated string for the policy user.const XC_CHAR*
pcDataElement[in] NULL terminated string for the data element.const XC_CHAR*
pcExternalIV[in] Buffer containing data that will be used as external IV.const XC_BYTE*
ui4ExternalIVLength[in] Number of bytes provided in the pcExternalIv buffer. The total amount of data for bulk protection should not exceed 256 bytes.const XC_UINT4
pInDataItems[in] Buffer containing the plain data to protect.const XC_DATA_ITEM_EX*
ui4InDataItemCount[in] Number of items contained in pInDataItems. The maximum number of data items should not exceed 1000 elements.const XC_UINT4
pOutDataItems[out] Buffer containing the protected data after a successful operation.XC_DATA_ITEM_EX*
pui4OutDataItemCount[in/out] On input the capacity of pOutDataItems which must be the same as ui4InDataItemCount.
On return it will contain the number of items placed in pOutDataItems.
XC_UINT4*
pi4ErrorIndex[out] Zero-based index of first item that failed (if any), otherwise it will be set to XC_ERROR_INDEX_NONE.XC_INT4*
pXCParam[in] Additional parameters.const XC_PARAM_EX*
ui4XCParamSize[in] Size of the additional parameters.const XC_UINT4
pstActionResult[out] Structure containing the result of the operation. For more information refer to the xcdefinitions.h file.stXC_ACTION_RESULT*

XCBulkReprotect Function

The XCBulkReprotect function reprotects the data in bulk from one data element to another. It will take a list of data items and send them in a single request to the Protegrity AP, which will then process them in a batch. It will then read the response from the Protegrity AP and return a list of data items containing the reprotected data.

This function returns XC_SUCCESS on success.

The following is a sample of the XCBulkReprotect function.

XCBulkReprotect( const XC_HANDLE          hXCHandle,
                 const XC_SESSION         hSession,
                 const XC_UINT4           ui4EventType,
                 const XC_CHAR*           pcPolicyUser,
                 const XC_CHAR*           pcNewDataElement,
                 const XC_CHAR*           pcOldDataElement,
                 const XC_BYTE*           pcOldExternalIV,
                 const XC_UINT4           ui4OldExternalIVLength,
                 const XC_BYTE*           pcNewExternalIV,
                 const XC_UINT4           ui4NewExternalIVLength,
                 const XC_DATA_ITEM_EX*   pInDataItems,
                 const XC_UINT4           ui4InDataItemCount,
                 XC_DATA_ITEM_EX*         pOutDataItems,
                 XC_UINT4*                pui4OutDataItemCount,
                 XC_INT4*                 pi4ErrorIndex,
                 const XC_PARAM_EX*       pXCParam,
                 const XC_UINT4           ui4XCParamSize,
                 stXC_ACTION_RESULT*      pstActionResult );

The following table lists the various parameters used for the XCBulkReprotect function.

ParameterDescriptionData Type
hXCHandle[in] Handle for the library that has been initialized.const XC_HANDLE
hSession[in] Handle for the established session.const XC_SESSION
ui4EventType[in] Event type for logging.const XC_UINT4
pcPolicyUser[in] String for the policy user.const XC_CHAR*
pcNewDataElement[in] String for the new data element.const XC_CHAR*
pcOldDataElement[in] String for the old data element.const XC_CHAR*
pcOldExternalIV[in] Buffer containing data that will be used as old external IV (can be NULL).const XC_BYTE*
ui4OldExternalIVLength[in] Length of old external IV.const XC_UINT4
pcNewExternalIV[in] Buffer containing data that will be used as new external IV (can be NULL).const XC_BYTE*
ui4NewExternalIVLength[in] Length of new external IV.const XC_UINT4
pInDataItems[in] Array of input data items.const XC_DATA_ITEM_EX*
ui4InDataItemCount[in] Number of input data items.const XC_UINT4
pOutDataItems[out] Array of output data items.XC_DATA_ITEM_EX*
pui4OutDataItemCount[in/out] Number of output data items.XC_UINT4*
pi4ErrorIndex[out] Index of the item that caused error.XC_INT4*
pXCParam[in] Additional parameters.const XC_PARAM_EX*
ui4XCParamSize[in] Size of the additional parameters.const XC_UINT4
pstActionResult[out] Action result structure.stXC_ACTION_RESULT*

XCCheckAccess Function

The XCCheckAccess function checks whether a policy user is permitted to perform an operation on a specific Data Element.

This function returns XC_SUCCESS if access is permitted and returns XC_ACCESS_DENIED if no access permitted.

The following is a sample for the XCCheckAccess function.

XCCheckAccess( const XC_HANDLE     hXCHandle,
               const XC_SESSION    hSession,
               const XC_UINT4      ui4Operation,
               const XC_CHAR*      pcPolicyUser,
               const XC_CHAR*      pcDataElement,
               const XC_CHAR*      pcOldDataElement,
               const XC_PARAM_EX*  pXCParam,
               const XC_UINT4      ui4XCParamSize );

The following table lists the various parameters used for the XCCheckAccess function.

ParameterDescriptionData Type
hXCHandle[in] Handle for the initialized library.const XC_HANDLE
hSession[in] Handle for the established session.const XC_SESSION
ui4Operation[in] Check operation to perform, one of XC_OPERATION_CHECK_PROTECT, XC_OPERATION_CHECK_UNPROTECT or XC_OPERATION_CHECK_REPROTECT.const XC_UINT4
pcPolicyUser[in] String for the policy user.const XC_CHAR*
pcDataElement[in] String for the data element to check (the new data element for reprotect).const XC_CHAR*
pcOldDataElement[in] String for the old data element, mandatory only for a reprotect check.const XC_CHAR*
pXCParam[in] Additional parameters.const XC_PARAM_EX*
ui4XCParamSize[in] Size of the additional parameters.const XC_UINT4

XCGetErrorDescription Function

The XCGetErrorDescription function gets error description when a previous call to a Protegrity AP function fails. To get the required size of the message buffer, you can call the function with pszMessage set to XC_NULL. The required buffer size will then be returned in parameter pui4MessageLength, and the return code set to XC_BUFFER_TOO_SMALL.

This function returns XC_SUCCESS if successful and it returns XC_BUFFER_TOO_SMALL if the pui4MessageLength is not big enough.

The following is a sample of the XCGetErrorDescription function.

XCGetErrorDescription( const XC_HANDLE  hXCHandle,
                       const XC_SESSION hSession,
                       XC_CHAR*         pszMessage,
                       XC_UINT4*        pui4MessageLength );

The following table lists the various parameters used for the XCGetErrorDescription function.

ParameterDescriptionData Type
hXCHandle[in] Handle for the library that has been initialized.const XC_HANDLE
hSession[in] Handle for the session or NULL if no session exists (for example, when XCOpenSession failed).const XC_SESSION
pszMessage[in/out] The null terminated message string.XC_CHAR*
pui4MessageLength[in/out] On input, it should specify the max length of the pszMessage buffer. On return, it will contain the length of the message (not counting null).XC_UINT4*

8 - Additional Topics

Learn about the AP C documentation with advanced operational insights and platform-specific guidance.

This section expands the core Application Protector (AP) C documentation.

  • Understanding AP C’s memory usage for different policy sizes helps with performance tuning and resource allocation.
  • The DevOps approach enables immutable package deployment using a REST API to download encrypted packages from the ESA.
  • Application Protector API return codes provide details on the status of API calls.
  • The config.ini file contains the configuration settings for Application Protector.
  • Multi-node Application Protector architecture describes how AP C operates across distributed nodes.
  • Uninstalling AP C on Linux covers the steps to remove all components, including the Log Forwarder.

8.1 - Memory Usage of the AP C

The memory usage in the AP C for different policy sizes with a sample.

The memory used for the different policy sizes using a sample AP C application is described in this section. This is a sample memory usage. You can use this as a reference for memory usage in the AP C for different policy sizes.

Sample application

For more information about the sample application, refer to samples installed in directory.

Expected memory usage

The process to find the policy size and expected memory usage for different policy sizes used by the C application is described in this section.

To find the policy size:

  1. On Insights dashboard, under the Discover section, navigate to the troubleshooting index.
  2. Search using the process.module.keyword: coreprovider filter.
  3. Navigate to the logs with description as Policy successfully loaded. The additional_info.memoryUsed field depicts the policy size.

Memory Usage

The following is the expected memory usage for different policy sizes used by the AP C application.

Policy sizeProcess memory consumption
14 MB42.13MB
37 MB94.98 MB
533 MB1.23 GB

The process memory increases substantially for a few milliseconds when the application is running in the following cases:

  • The policy is replaced with another policy
  • Changes are made in the current policy

Conclusion

The results for memory required by various policy sizes using the sample AP C application can be used to determine the memory requirements of the C application.

8.2 - DevOps Approach for Application Protector C

The DevOps approach for package deployment.

The DevOps approach enables immutable package deployment. It uses a REST API call to download packages from the ESA in an encrypted format.

Note: The RP Agent should not be installed for immutable package deployments using DevOps.

For more information about package deployment approaches, refer to Resilient Package Deployment.

A REST API call is used to download the package on your local machine. Configure the package path in the config.ini file within the DevOps section and the decryptor shared library.

If a downloaded path is overwritten, a new package will be reflected in the running application at the set time interval (cadence). This occurs when another package with the same name overwrites the existing one. This changes the protector’s behavior. The protector no longer functions as an immutable protector. To keep it immutable, set the cadence value in config.ini file to 0.

DevOps approach architecture

  1. A REST API call is used to download the policy from the ESA in an envelope encrypted format. A public key is created using a Key Management System (KMS) or Hardware Security Module (HSM). This public key must be passed to the REST API.
  2. The ESA generates a JSON file for the package with policy.
  3. The encrypted DEK needs to be decrypted to perform the security operations. A Decryptor class is implemented using the Decryptor interface, to decrypt the Data Encryption Key (DEK) using a private key.

Before you begin

Ensure the following prerequisites are met:

  • The installation of the RP Agent is not required for immutable package deployment using the DevOps approach.
  • The decryptor parameter must be the full path to the decryptor shared library.
    A decryptor shared library must implement the decrypt() function, which decrypts the Data Encryption Key (DEK) using a private key. It returns the decrypted DEK in bytes.
    For more information on the decryptor interface of AP C, refer to Configuring the Decryptor interface.
  • The data store is properly configured before exporting your Application Protector policy. For ESA 10.0.1 and 10.1.0 versions, define allowed servers for seamless policy deployment and secure access control.
    For more information about configuring a data store, refer to -
  • For ESA version 10.2.0 and higher, export key must be created in the data store for secure access control. For more information refer to Using the Encrypted Resilient Package REST APIs.

AP C

Using the DevOps approach

Perform the following steps to use the DevOps approach for immutable package deployment.

  1. Create the RSA decryptor file, for example: rsa_decryptor.c, using decrypt() function and then compile it which will create a .so shared library. Example -

    gcc -shared -fPIC -o /opt/apc/rsa_decryptor.so /opt/apc/rsa_decryptor.c -lssl -lcrypto
    
  2. Add the [devops] parameter in the config.ini file.

    [devops]
    package.path = /path/to/policyFile
    decryptor = /path/to/decryptor_shared_library.so
    

    The following is an example for adding the [devops] parameter in the config.ini file.

    [devops]
    package.path = /opt/policies/policy1.json
    decryptor = /opt/apc/rsa_decryptor.so
    

Note: For ESA 10.2.0 and later, Application Protector DevOps must use the Encrypted Resilient Package REST APIs using GET method. The legacy Export API using POST method is deprecated and not supported for Teams (PPC). The deprecated API remains supported only for the Enterprise edition for backward compatibility.

For more information about exporting Resilient Package using POST method for 10.0.1 and 10.1.0 ESA, refer to Using the Encrypted Resilient Package REST APIs.

For more information about exporting Resilient Package using GET method for 10.2 ESA, refer to Using the Encrypted Resilient Package REST APIs.

For more information about exporting Resilient Package using GET method for PPC, refer to Using the Encrypted Resilient Package REST APIs.

Sample code for DevOps approach

The sample code for DevOps approach for various Application Protectors using different cloud platforms is provided in this section.

DevOps approach for AP C

The sample code for DevOps approach for the AP C using different cloud platforms is provided in this section.

Configuring the Decryptor interface

The decrypt function provides key_label, algorithm, encrypted_dek, and encrypted_dek_len parameters. The decrypted DEK must be returned as an unsigned char *, and its length written to *decrypted_dek_len. The path to the compiled .so is set using decryptor= in the [devops] section of config.ini.

Custom DEK Decryptor Implementation Requirements:

  1. Exact function signature — must match precisely, including parameter names and types:

    unsigned char *decrypt(char *key_label, const unsigned char *encrypted_dek,
                           char *algorithm, int encrypted_dek_len, int *decrypted_dek_len);
    
  2. Include the header#include "decryptor.h" is required; the AP C runtime provides this header.

  3. Memory allocation — the return value must be allocated with OPENSSL_malloc(), not malloc(), because the APC runtime frees it with OPENSSL_free().

  4. Return contract — return NULL on failure; on success write the plaintext length to *decrypted_dek_len and return the plaintext buffer.

  5. Algorithm strings — the algorithm parameter will be one of: RSA_OAEP_SHA1, RSA_OAEP_SHA256, RSA_OAEP_SHA512, RSA_PKCS1_v1_5. The code must handle whichever algorithm was used to encrypt the DEK.

  6. Private key path — hardcode or configure the path to the private key (PKCS#8 PEM format) inside the .c file before compiling.

  7. Build — compile as a shared library:

    gcc -shared -fPIC -o rsa_decryptor.so rsa_decryptor.c -lssl -lcrypto
    
  8. Register the .so — set decryptor=<absolute_path_to_rsa_decryptor.so> in the [devops] section of config.ini.

Using AWS

The following is a sample implementation using the private key from AWS KMS.

/*
 * Prerequisites to build and run this file:
 *
 * 1. Install AWS C++ SDK (kms + core only):
 *      cmake .. -DBUILD_ONLY="kms;core"
 *      make && sudo make install
 *    Ref: GitHub - aws/aws-sdk-cpp: AWS SDK for C++
 *
 * 2. Set LD_LIBRARY_PATH to include AWS SDK and APC libraries:
 *      export LD_LIBRARY_PATH=/home/ec2-user/c_sdk/apc/build/lib:/usr/local/lib64
 *    Ensure libaws-cpp-sdk-kms.so and libaws-cpp-sdk-core.so are present.
 *
 * 3. Configure AWS credentials (one of):
 *    a) CLI:  aws configure
 *    b) Env:  export AWS_ACCESS_KEY_ID="..."
 *             export AWS_SECRET_ACCESS_KEY="..."
 *             export AWS_REGION="..."
 *
 * 4. Ensure the KMS key (RSA_4096, ENCRYPT_DECRYPT) exists in your AWS account.
 *    Retrieve the key ID from AWS KMS Console or:
 *      aws kms get-public-key --key-id <your-key-id>
 *
 * 5. Compile:
 *      g++ -g -shared -o aws.so test.cpp -lcrypto -laws-cpp-sdk-kms -laws-cpp-sdk-core -fPIC
 */
#include <aws/core/Aws.h>
#include <aws/kms/KMSClient.h>
#include <aws/kms/model/DecryptRequest.h>
#include "../../../apc/decryptor.h"

extern "C"{
unsigned char* decrypt(char* key_label, const unsigned char *encrypted_dek, char* algorithm, int encrypted_dek_len, int *decrypted_dek_len)
{  
    Aws::SDKOptions options;
    /* Initialize AWS SDK*/
    Aws::InitAPI(options);
    Aws::Client::ClientConfiguration clientConfig;
    /* Disable SSL verification */
    clientConfig.verifySSL = false; 
    /* Create KMS Client */
    Aws::KMS::KMSClient kms(clientConfig);
    /* Create Decrypt Request*/
    Aws::KMS::Model::DecryptRequest decrypt_request;
    unsigned char* decrypted_data = nullptr;
    decrypt_request.SetCiphertextBlob(Aws::Utils::ByteBuffer(encrypted_dek, encrypted_dek_len));
    decrypt_request.WithEncryptionAlgorithm(Aws::KMS::Model::EncryptionAlgorithmSpec::RSAES_OAEP_SHA_256);
    decrypt_request.WithKeyId("3068b3ef-4924-4be5-9e9a-440b418553b3");
    auto decryptOutcome = kms.Decrypt(decrypt_request);
    if(!decryptOutcome.IsSuccess()){
       std::cerr << "Error Decrypting data: " << decryptOutcome.GetError().GetMessage() << std::endl;    
    }
    Aws::Utils::ByteBuffer plaintext = decryptOutcome.GetResult().GetPlaintext();
    /* Allocate memory for decrypted data */
    decrypted_data = new unsigned char[plaintext.GetLength()];
    std::memcpy(decrypted_data, plaintext.GetUnderlyingData(), plaintext.GetLength());
    *decrypted_dek_len = plaintext.GetLength();   
    return decrypted_data;
}
}
Using Azure

The following is a sample implementation using the private key from Azure Key Vault.

/*
 * Prerequisites to build and run this file:
 *
 * 1. Install Azure SDK and the dependencies required by azure.cpp.
 *
 * 2. Log in to Azure:
 *      az login
 *
 *      Verify the active subscription:
 *      az account show
 *
 * 3. Create an Azure Key Vault:
 *      az keyvault create \
 *        --name <key-vault-name> \
 *        --resource-group <resource-group> \
 *        --location <location>
 *
 * 4. Create an RSA key in the Key Vault:
 *      az keyvault key create \
 *        --vault-name <key-vault-name> \
 *        --name <key-name> \
 *        --kty RSA \
 *        --size 2048 \
 *        --ops encrypt decrypt wrapKey unwrapKey sign verify
 *
 * 5. Get the VM Managed Identity Object ID:
 *      az vm identity show \
 *        --resource-group <resource-group> \
 *        --name <vm-name> \
 *        --query principalId -o tsv
 *
 * 6. Grant Key Vault permissions:
 *      az keyvault set-policy \
 *        --name <key-vault-name> \
 *        --object-id <principal-id> \
 *        --key-permissions get list decrypt unwrapKey
 *
 * 7. Get the Key Vault key URI (key_label):
 *      az keyvault key show \
 *        --vault-name <key-vault-name> \
 *        --name <key-name> \
 *        --query "key.kid" -o tsv
 *
 *      Example:
 *      https://<key-vault-name>.vault.azure.net/keys/<key-name>/<version>
 *
 * 8. Download the public key:
 *      az keyvault key download \
 *        --vault-name <key-vault-name> \
 *        --name <key-name> \
 *        --file <public-key>.pem \
 *        --encoding PEM
 *
 * 9. Export the APC policy package:
 *      curl -k -u <user>:<password> -X POST \
 *        'https://<rps-host>/pty/v1/rps/export?version=1&coreversion=1' \
 *        -H 'Content-Type: application/json' \
 *        -d '{
 *              "kek": {
 *                "publicKey": {
 *                  "label": "<key_label>",
 *                  "algorithm": "RSA-OAEP-256",
 *                  "value": "-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----"
 *                }
 *              }
 *            }' \
 *        -o pkg_azure.json
 *
 * 10. Compile:
 *      g++ -std=c++17 -fPIC -shared -o azure.so azure.cpp \
 *          -I<azure-sdk-include-dir> \
 *          -I<apc-source-dir> \
 *          -lcurl \
 *          -lpthread
 *
 * 11. Set environment variables:
 *      export LD_LIBRARY_PATH=<apc-library-dir>:$LD_LIBRARY_PATH
 *      export PTY_APC_CONFIG=<config-file>
 *
 * 12. Run:
 *      ./xcapcsample -p <parameter> -u <user> -d1 <data-element-name>
 */
/*******************************************************************************
 * azure.cpp
 *
 * Azure Key Vault decryptor plug-in  C++ implementation via REST API.
 *
 * Calls the Azure Key Vault decrypt REST endpoint using libcurl and OpenSSL
 * for base64url encoding/decoding.  No Azure C++ SDK required.
 *
 * key_label must be the full Azure Key Vault key identifier URL:
 *   https://<vault-name>.vault.azure.net/keys/<key-name>/<key-version>
 *
 * Authentication (in priority order):
 *   1. AZURE_ACCESS_TOKEN env var    pre-obtained Bearer token.
 *   2. Client credentials flow       set AZURE_TENANT_ID, AZURE_CLIENT_ID,
 *                                     AZURE_CLIENT_SECRET env vars.
 *   3. Azure Managed Identity (IMDS) works on Azure VMs / AKS / App Service.
 *
 * Build:
 *   g++ -std=c++17 -fPIC -shared -o azure.so azure.cpp \
 *       -I<path-to-apc-includes> \
 *       -lcurl -lssl -lcrypto
 ******************************************************************************/

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <vector>

#include <curl/curl.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>

#include "../../../apc/decryptor.h"

/* ==========================================================================
 * Internal helpers
 * ========================================================================== */

namespace {

/* --------------------------------------------------------------------------
 * libcurl write callback  appends received data to a std::string.
 * -------------------------------------------------------------------------- */
std::size_t curlWriteCallback(char* ptr, std::size_t size,
                               std::size_t nmemb, void* userdata)
{
    auto* buf = static_cast<std::string*>(userdata);
    buf->append(ptr, size * nmemb);
    return size * nmemb;
}

/* --------------------------------------------------------------------------
 * base64urlEncode
 *
 * Azure Key Vault uses base64url (RFC 4648 §5): '+'  '-', '/'  '_',
 * no '=' padding.
 * -------------------------------------------------------------------------- */
std::string base64urlEncode(const unsigned char* src, int len)
{
    /* Standard base64 first */
    BIO* b64 = BIO_new(BIO_f_base64());
    BIO* mem = BIO_new(BIO_s_mem());
    BUF_MEM* bptr = nullptr;

    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
    BIO_push(b64, mem);
    BIO_write(b64, src, len);
    BIO_flush(b64);
    BIO_get_mem_ptr(b64, &bptr);
    BIO_set_close(b64, BIO_NOCLOSE);
    BIO_free_all(b64);

    std::string result(bptr->data, bptr->length);
    BUF_MEM_free(bptr);

    /* Convert to base64url */
    for (char& c : result) {
        if (c == '+') c = '-';
        else if (c == '/') c = '_';
    }
    /* Strip padding */
    while (!result.empty() && result.back() == '=')
        result.pop_back();

    return result;
}

/* --------------------------------------------------------------------------
 * base64urlDecode
 *
 * Converts base64url back to standard base64 then decodes.
 * -------------------------------------------------------------------------- */
std::vector<unsigned char> base64urlDecode(const std::string& src)
{
    std::string b64 = src;

    /* Convert base64url  standard base64 */
    for (char& c : b64) {
        if (c == '-') c = '+';
        else if (c == '_') c = '/';
    }
    /* Re-add padding */
    while (b64.size() % 4 != 0)
        b64 += '=';

    std::vector<unsigned char> out(b64.size());
    BIO* bmem = BIO_new_mem_buf(b64.data(), static_cast<int>(b64.size()));
    BIO* b64bio = BIO_new(BIO_f_base64());
    BIO_set_flags(b64bio, BIO_FLAGS_BASE64_NO_NL);
    BIO_push(b64bio, bmem);

    int n = BIO_read(b64bio, out.data(), static_cast<int>(b64.size()));
    BIO_free_all(b64bio);

    if (n <= 0)
        return {};
    out.resize(static_cast<std::size_t>(n));
    return out;
}

/* --------------------------------------------------------------------------
 * jsonGetString
 *
 * Extracts the value of a flat JSON string field.
 * Handles optional whitespace around ':'.
 * -------------------------------------------------------------------------- */
std::string jsonGetString(const std::string& json, const std::string& field)
{
    const std::string key = "\"" + field + "\"";
    auto pos = json.find(key);
    if (pos == std::string::npos)
        return {};

    pos += key.size();

    /* Skip whitespace then ':' */
    while (pos < json.size() && (json[pos] == ' ' || json[pos] == '\t' ||
                                  json[pos] == '\r' || json[pos] == '\n'))
        ++pos;
    if (pos >= json.size() || json[pos] != ':')
        return {};
    ++pos;

    /* Skip whitespace after ':' */
    while (pos < json.size() && (json[pos] == ' ' || json[pos] == '\t' ||
                                  json[pos] == '\r' || json[pos] == '\n'))
        ++pos;
    if (pos >= json.size() || json[pos] != '"')
        return {};
    ++pos; /* skip opening '"' */

    auto end = json.find('"', pos);
    if (end == std::string::npos)
        return {};
    return json.substr(pos, end - pos);
}

/* --------------------------------------------------------------------------
 * getTokenClientCredentials
 *
 * Fetches an Azure AD Bearer token using the client credentials OAuth2 flow.
 * Requires: AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET env vars.
 * -------------------------------------------------------------------------- */
std::string getTokenClientCredentials()
{
    const char* tenantId     = std::getenv("AZURE_TENANT_ID");
    const char* clientId     = std::getenv("AZURE_CLIENT_ID");
    const char* clientSecret = std::getenv("AZURE_CLIENT_SECRET");

    if (!tenantId || !clientId || !clientSecret ||
        tenantId[0] == '\0' || clientId[0] == '\0' || clientSecret[0] == '\0')
        return {};

    const std::string tokenUrl =
        "https://login.microsoftonline.com/" + std::string(tenantId) +
        "/oauth2/v2.0/token";

    const std::string body =
        "grant_type=client_credentials"
        "&client_id="     + std::string(clientId) +
        "&client_secret=" + std::string(clientSecret) +
        "&scope=https%3A%2F%2Fvault.azure.net%2F.default";

    std::string response;
    CURL* curl = curl_easy_init();
    if (!curl) return {};

    curl_easy_setopt(curl, CURLOPT_URL,           tokenUrl.c_str());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS,    body.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA,     &response);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT,       10L);

    CURLcode rc = curl_easy_perform(curl);
    curl_easy_cleanup(curl);

    if (rc != CURLE_OK) {
        std::cerr << "[Azure KV] token request failed: "
                  << curl_easy_strerror(rc) << std::endl;
        return {};
    }

    /* {"access_token":"<TOKEN>","token_type":"Bearer",...} */
    std::string token = jsonGetString(response, "access_token");
    if (token.empty())
        std::cerr << "[Azure KV] unexpected token response: " << response << std::endl;
    return token;
}

/* --------------------------------------------------------------------------
 * getTokenManagedIdentity
 *
 * Fetches a Bearer token from the Azure IMDS endpoint (Azure VMs / AKS).
 * -------------------------------------------------------------------------- */
std::string getTokenManagedIdentity()
{
    std::string response;
    CURL* curl = curl_easy_init();
    if (!curl) return {};

    curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, "Metadata: true");

    curl_easy_setopt(curl, CURLOPT_URL,
        "http://169.254.169.254/metadata/identity/oauth2/token"
        "?api-version=2018-02-01&resource=https://vault.azure.net");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER,    headers);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA,     &response);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT,       5L);

    CURLcode rc = curl_easy_perform(curl);
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    if (rc != CURLE_OK) {
        std::cerr << "[Azure KV] IMDS token fetch failed: "
                  << curl_easy_strerror(rc) << std::endl;
        return {};
    }

    std::string token = jsonGetString(response, "access_token");
    if (token.empty())
        std::cerr << "[Azure KV] unexpected IMDS response: " << response << std::endl;
    return token;
}

/* --------------------------------------------------------------------------
 * getAccessToken
 *
 * Returns a Bearer token for Azure Key Vault API calls.
 *
 * Priority:
 *   1. AZURE_ACCESS_TOKEN env var   (any environment)
 *   2. Client credentials flow      (AZURE_TENANT_ID + CLIENT_ID + SECRET)
 *   3. Azure Managed Identity IMDS  (Azure VMs / AKS / App Service)
 * -------------------------------------------------------------------------- */
std::string getAccessToken()
{
    const char* env = std::getenv("AZURE_ACCESS_TOKEN");
    if (env && env[0] != '\0')
        return std::string(env);

    std::string token = getTokenClientCredentials();
    if (!token.empty())
        return token;

    token = getTokenManagedIdentity();
    if (!token.empty())
        return token;

    std::cerr << "[Azure KV] could not obtain access token. Set "
                 "AZURE_ACCESS_TOKEN, or AZURE_TENANT_ID + AZURE_CLIENT_ID + "
                 "AZURE_CLIENT_SECRET, or run on an Azure VM with Managed Identity."
              << std::endl;
    return {};
}

} /* anonymous namespace */

/* ==========================================================================
 * decrypt  public interface
 * ========================================================================== */

extern "C" {

/*******************************************************************************
 * decrypt
 *
 * Decrypts an RSA-OAEP-wrapped DEK via Azure Key Vault decrypt REST API.
 *
 * Parameters:
 *   key_label         - Full Azure Key Vault key identifier URL:
 *                       https://<vault>.vault.azure.net/keys/<key>/<version>
 *   encrypted_dek     - Raw ciphertext bytes.
 *   algorithm         - Encryption algorithm (e.g. "RSA-OAEP-256").
 *                       Passed directly to the Azure KV API.
 *   encrypted_dek_len - Length of encrypted_dek in bytes.
 *   decrypted_dek_len - Out: length of the returned plaintext buffer.
 *
 * Returns:
 *   Heap-allocated plaintext on success (caller must free with delete[]),
 *   or nullptr on failure.
 ******************************************************************************/
unsigned char* decrypt(char*                key_label,
                       const unsigned char* encrypted_dek,
                       char*                algorithm,
                       int                  encrypted_dek_len,
                       int*                 decrypted_dek_len)
{
    if (!key_label || !encrypted_dek || encrypted_dek_len <= 0 || !decrypted_dek_len)
    {
        std::cerr << "[Azure KV] decrypt: invalid argument(s)." << std::endl;
        return nullptr;
    }

    *decrypted_dek_len = 0;

    /* -- 1. Determine algorithm ------------------------------------------- */
    /* Normalize algorithm name to Azure Key Vault format.
     * xcpep.plm passes underscore-style names (e.g. RSA_OAEP_SHA256) but
     * Azure KV REST API expects hyphen-style (e.g. RSA-OAEP-256). */
    auto normalizeAlg = [](const std::string& a) -> std::string {
        if (a == "RSA_OAEP_SHA256" || a == "RSA-OAEP-SHA256") return "RSA-OAEP-256";
        if (a == "RSA_OAEP_SHA384" || a == "RSA-OAEP-SHA384") return "RSA-OAEP-384";
        if (a == "RSA_OAEP_SHA512" || a == "RSA-OAEP-SHA512") return "RSA-OAEP-512";
        if (a == "RSA_OAEP" || a == "RSA_OAEP_SHA1")           return "RSA-OAEP";
        if (a == "RSA_PKCS1")                                   return "RSA1_5";
        return a; /* pass through if already correct or unknown */
    };
    const std::string alg = normalizeAlg(
        (algorithm && algorithm[0] != '\0') ? std::string(algorithm) : "RSA-OAEP-256"
    );

    /* -- 2. Base64url-encode the ciphertext ------------------------------- */
    const std::string b64urlCipher = base64urlEncode(encrypted_dek, encrypted_dek_len);

    /* -- 3. Obtain a Bearer token ----------------------------------------- */
    const std::string token = getAccessToken();
    if (token.empty())
        return nullptr;

    /* -- 4. Build request URL --------------------------------------------- */
    /* https://<vault>.vault.azure.net/keys/<key>/<ver>/decrypt?api-version=7.4 */
    const std::string url = std::string(key_label) + "/decrypt?api-version=7.4";

    /* -- 5. Build JSON request body --------------------------------------- */
    /* {"alg":"RSA-OAEP-256","value":"<base64url-ciphertext>"} */
    const std::string body =
        "{\"alg\":\"" + alg + "\","
        "\"value\":\"" + b64urlCipher + "\"}";

    const std::string authHdr = "Authorization: Bearer " + token;

    /* -- 6. POST to Azure Key Vault decrypt endpoint ---------------------- */
    std::string response;
    CURL* curl = curl_easy_init();
    if (!curl)
    {
        std::cerr << "[Azure KV] curl_easy_init failed." << std::endl;
        return nullptr;
    }

    curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, "Content-Type: application/json");
    headers = curl_slist_append(headers, authHdr.c_str());

    curl_easy_setopt(curl, CURLOPT_URL,           url.c_str());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS,    body.c_str());
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER,    headers);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA,     &response);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT,       30L);

    CURLcode rc = curl_easy_perform(curl);

    long httpCode = 0;
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    if (rc != CURLE_OK)
    {
        std::cerr << "[Azure KV] HTTP request failed: "
                  << curl_easy_strerror(rc) << std::endl;
        return nullptr;
    }
    if (httpCode != 200)
    {
        std::cerr << "[Azure KV] HTTP " << httpCode << ": " << response << std::endl;
        return nullptr;
    }

    /* -- 7. Extract "value" (plaintext) from JSON response ---------------- */
    /* Response: {"kid":"...","value":"<base64url-plaintext>"} */
    const std::string b64urlPlain = jsonGetString(response, "value");
    if (b64urlPlain.empty())
    {
        std::cerr << "[Azure KV] could not parse 'value' field: "
                  << response << std::endl;
        return nullptr;
    }

    /* -- 8. Base64url-decode the plaintext -------------------------------- */
    std::vector<unsigned char> plaintext = base64urlDecode(b64urlPlain);
    if (plaintext.empty())
    {
        std::cerr << "[Azure KV] base64url decode of plaintext failed." << std::endl;
        return nullptr;
    }

    /* -- 9. Return caller-owned buffer ------------------------------------ */
    unsigned char* out = new unsigned char[plaintext.size()];
    std::memcpy(out, plaintext.data(), plaintext.size());
    *decrypted_dek_len = static_cast<int>(plaintext.size());
    return out;
}

} /* extern "C" */
Using GCP

The following is a sample implementation using the private key from Google Cloud KMS.

/*
 * Prerequisites to build and run this file:
 *
 * 1. Install Google Cloud SDK and required dependencies.
 *
 * 2. Authenticate with GCP:
 *      gcloud init
 *
 *      Verify active account:
 *      gcloud auth list
 *
 * 3. Create a KMS Key Ring:
 *      gcloud kms keyrings create <key-ring-name> \
 *          --location global
 *
 * 4. Create an Asymmetric RSA Key:
 *      gcloud kms keys create <key-name> \
 *          --location global \
 *          --keyring <key-ring-name> \
 *          --purpose asymmetric-encryption \
 *          --default-algorithm rsa-decrypt-oaep-2048-sha256
 *
 * 5. Get the Key Label:
 *      gcloud kms keys versions list \
 *          --location global \
 *          --keyring <key-ring-name> \
 *          --key <key-name>
 *
 *      Example:
 *      projects/<project-id>/locations/global/keyRings/<key-ring-name>/cryptoKeys/<key-name>/cryptoKeyVersions/1
 *
 * 6. Download the public key:
 *      gcloud kms keys versions get-public-key 1 \
 *          --location global \
 *          --keyring <key-ring-name> \
 *          --key <key-name> \
 *          --output-file gcp_public_key.pem
 *
 * 7. Export the APC policy package:
 *      curl -k -u <user>:<password> -X POST \
 *        'https://<rps-host>/pty/v1/rps/export?version=1&coreversion=1' \
 *        -H 'Content-Type: application/json' \
 *        -d '{
 *              "kek": {
 *                "publicKey": {
 *                  "label": "<key-label>",
 *                  "algorithm": "RSA-OAEP-256",
 *                  "value": "-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----"
 *                }
 *              }
 *            }' \
 *        -o pkg_gcp.json
 *
 * 8. Generate an access token:
 *      export GOOGLE_ACCESS_TOKEN=$(gcloud auth print-access-token)
 *
 * 9. Compile:
 *      g++ -std=c++17 -fPIC -shared -o gcp.so gcp.cpp \
 *          -I<apc-source-dir> \
 *          -lcurl -lssl -lcrypto
 *
 * 10. Build xapcsample:
 *      cc -g -o xcapcsample -DUNIX \
 *          -I<apc-include-dir> \
 *          -L<apc-library-dir> \
 *          -lxcpep \
 *          xcapcsample.c
 *
 * 11. Set environment variables:
 *      export LD_LIBRARY_PATH=<apc-library-dir>:$LD_LIBRARY_PATH
 *      export PTY_APC_CONFIG=<config-file>
 *
 * 12. Run:
 *      ./xcapcsample -p <parameter> -u <user> -d1 <data-element-name>
 */
/*******************************************************************************
 * gcp.cpp
 *
 * GCP Cloud KMS decryptor plug-in  C++ implementation via REST API.
 *
 * Calls the GCP KMS AsymmetricDecrypt REST endpoint using libcurl and
 * OpenSSL for base64.  No google-cloud-cpp SDK required.
 *
 * key_label must be the full CryptoKeyVersion resource name:
 *   projects/<P>/locations/<L>/keyRings/<R>/cryptoKeys/<K>/cryptoKeyVersions/<V>
 *
 * Authentication (in priority order):
 *   1. GOOGLE_ACCESS_TOKEN env var   any environment.
 *   2. GCE metadata server           GCE / GKE / Cloud Run.
 *      Requires roles/cloudkms.cryptoKeyDecrypter on the service account.
 *
 * Build:
 *   g++ -std=c++17 -fPIC -shared -o gcp.so gcp.cpp \
 *       -I<path-to-apc-includes> \
 *       -lcurl -lssl -lcrypto
 ******************************************************************************/

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>

#include <curl/curl.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>

#include "../../../apc/decryptor.h"

/* ==========================================================================
 * Internal helpers
 * ========================================================================== */

namespace {

/* --------------------------------------------------------------------------
 * libcurl write callback  appends received data to a std::string.
 * -------------------------------------------------------------------------- */
std::size_t curlWriteCallback(char* ptr, std::size_t size,
                               std::size_t nmemb, void* userdata)
{
    auto* buf = static_cast<std::string*>(userdata);
    buf->append(ptr, size * nmemb);
    return size * nmemb;
}

/* --------------------------------------------------------------------------
 * base64Encode  standard base64, no line breaks.
 * -------------------------------------------------------------------------- */
std::string base64Encode(const unsigned char* src, int len)
{
    BIO* b64 = BIO_new(BIO_f_base64());
    BIO* mem = BIO_new(BIO_s_mem());
    BUF_MEM* bptr = nullptr;

    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
    BIO_push(b64, mem);
    BIO_write(b64, src, len);
    BIO_flush(b64);
    BIO_get_mem_ptr(b64, &bptr);
    BIO_set_close(b64, BIO_NOCLOSE);
    BIO_free_all(b64);

    std::string result(bptr->data, bptr->length);
    BUF_MEM_free(bptr);
    return result;
}

/* --------------------------------------------------------------------------
 * base64Decode  standard base64, no line breaks.
 * -------------------------------------------------------------------------- */
std::vector<unsigned char> base64Decode(const std::string& src)
{
    std::vector<unsigned char> out(src.size());
    BIO* bmem = BIO_new_mem_buf(src.data(), static_cast<int>(src.size()));
    BIO* b64  = BIO_new(BIO_f_base64());
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
    BIO_push(b64, bmem);

    int n = BIO_read(b64, out.data(), static_cast<int>(src.size()));
    BIO_free_all(b64);

    if (n <= 0)
        return {};
    out.resize(static_cast<std::size_t>(n));
    return out;
}

/* --------------------------------------------------------------------------
 * jsonGetString  extracts the value of a flat JSON string field.
 * Handles optional whitespace between ':' and the opening '"'.
 * -------------------------------------------------------------------------- */
std::string jsonGetString(const std::string& json, const std::string& field)
{
    /* Search for "field" : "  (with optional spaces around colon) */
    const std::string key = "\"" + field + "\"";
    auto pos = json.find(key);
    if (pos == std::string::npos)
        return {};

    pos += key.size();

    /* Skip whitespace and the colon */
    while (pos < json.size() && (json[pos] == ' ' || json[pos] == '\t' || json[pos] == '\r' || json[pos] == '\n'))
        ++pos;
    if (pos >= json.size() || json[pos] != ':')
        return {};
    ++pos; /* skip ':' */

    /* Skip whitespace after colon */
    while (pos < json.size() && (json[pos] == ' ' || json[pos] == '\t' || json[pos] == '\r' || json[pos] == '\n'))
        ++pos;
    if (pos >= json.size() || json[pos] != '"')
        return {};
    ++pos; /* skip opening '"' */

    auto end = json.find('"', pos);
    if (end == std::string::npos)
        return {};
    return json.substr(pos, end - pos);
}

/* --------------------------------------------------------------------------
 * getAccessToken  fetches a Bearer token.
 *   1. GOOGLE_ACCESS_TOKEN env var.
 *   2. GCE instance metadata server.
 * -------------------------------------------------------------------------- */
std::string getAccessToken()
{
    const char* env = std::getenv("GOOGLE_ACCESS_TOKEN");
    if (env && env[0] != '\0')
        return std::string(env);

    /* Query GCE metadata server. */
    std::string response;
    CURL* curl = curl_easy_init();
    if (!curl)
        return {};

    curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, "Metadata-Flavor: Google");

    curl_easy_setopt(curl, CURLOPT_URL,
        "http://metadata.google.internal/computeMetadata/v1"
        "/instance/service-accounts/default/token");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER,    headers);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA,     &response);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT,       5L);

    CURLcode rc = curl_easy_perform(curl);
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    if (rc != CURLE_OK) {
        std::cerr << "[GCP KMS] metadata token fetch failed: "
                  << curl_easy_strerror(rc) << std::endl;
        return {};
    }

    /* {"access_token":"<TOKEN>","expires_in":...,"token_type":"Bearer"} */
    std::string token = jsonGetString(response, "access_token");
    if (token.empty())
        std::cerr << "[GCP KMS] unexpected token response: " << response << std::endl;
    return token;
}

} /* anonymous namespace */

/* ==========================================================================
 * decrypt  public interface
 * ========================================================================== */

extern "C" {

/*******************************************************************************
 * decrypt
 *
 * Decrypts an RSA-OAEP-wrapped DEK via GCP Cloud KMS AsymmetricDecrypt REST.
 *
 * Parameters:
 *   key_label         - Full GCP KMS CryptoKeyVersion resource name.
 *   encrypted_dek     - Raw ciphertext bytes.
 *   algorithm         - Algorithm string (informational; derived by GCP KMS
 *                       from the key version).
 *   encrypted_dek_len - Length of encrypted_dek in bytes.
 *   decrypted_dek_len - Out: length of the returned plaintext buffer.
 *
 * Returns:
 *   Heap-allocated plaintext on success (caller must free with delete[]),
 *   or nullptr on failure.
 ******************************************************************************/
unsigned char* decrypt(char*                key_label,
                       const unsigned char* encrypted_dek,
                       char*                algorithm,
                       int                  encrypted_dek_len,
                       int*                 decrypted_dek_len)
{
    if (!key_label || !encrypted_dek || encrypted_dek_len <= 0 || !decrypted_dek_len)
    {
        std::cerr << "[GCP KMS] decrypt: invalid argument(s)." << std::endl;
        return nullptr;
    }

    *decrypted_dek_len = 0;

    /* -- 1. Base64-encode the ciphertext ---------------------------------- */
    const std::string b64Cipher = base64Encode(encrypted_dek, encrypted_dek_len);

    /* -- 2. Obtain a Bearer token ----------------------------------------- */
    const std::string token = getAccessToken();
    if (token.empty())
    {
        std::cerr << "[GCP KMS] failed to obtain access token." << std::endl;
        return nullptr;
    }

    /* -- 3. Build request body and headers -------------------------------- */
    const std::string body    = "{\"ciphertext\":\"" + b64Cipher + "\"}";
    const std::string authHdr = "Authorization: Bearer " + token;
    const std::string url     = "https://cloudkms.googleapis.com/v1/" +
                                 std::string(key_label) + ":asymmetricDecrypt";

    /* -- 4. POST to GCP KMS REST endpoint --------------------------------- */
    std::string response;
    CURL* curl = curl_easy_init();
    if (!curl)
    {
        std::cerr << "[GCP KMS] curl_easy_init failed." << std::endl;
        return nullptr;
    }

    curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, "Content-Type: application/json");
    headers = curl_slist_append(headers, authHdr.c_str());

    curl_easy_setopt(curl, CURLOPT_URL,           url.c_str());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS,    body.c_str());
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER,    headers);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA,     &response);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT,       30L);

    CURLcode rc = curl_easy_perform(curl);

    long httpCode = 0;
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    if (rc != CURLE_OK)
    {
        std::cerr << "[GCP KMS] HTTP request failed: "
                  << curl_easy_strerror(rc) << std::endl;
        return nullptr;
    }
    if (httpCode != 200)
    {
        std::cerr << "[GCP KMS] HTTP " << httpCode << ": " << response << std::endl;
        return nullptr;
    }

    /* -- 5. Extract and decode plaintext from JSON response --------------- */
    const std::string b64Plain = jsonGetString(response, "plaintext");
    if (b64Plain.empty())
    {
        std::cerr << "[GCP KMS] could not parse 'plaintext' field: "
                  << response << std::endl;
        return nullptr;
    }

    std::vector<unsigned char> plaintext = base64Decode(b64Plain);
    if (plaintext.empty())
    {
        std::cerr << "[GCP KMS] base64 decode of plaintext failed." << std::endl;
        return nullptr;
    }

    /* -- 6. Return caller-owned buffer ------------------------------------ */
    unsigned char* out = new unsigned char[plaintext.size()];
    std::memcpy(out, plaintext.data(), plaintext.size());
    *decrypted_dek_len = static_cast<int>(plaintext.size());
    return out;
}

} /* extern "C" */

8.3 - Application Protector API Return Codes

Learn about the Application Protector API Return Codes.

When an application is developed using the APIs of the Protegrity Application Protector Suite, you may encounter the Application Protector API Return Codes. For more information about log return codes, refer to Log return codes.

Sample Log for AP Return Codes

The following is a sample log generated in Discover on the Audit Store Dashboards in the ESA.

Sample log for AP return codes

Protection audit logs are stored in the Audit Store. Select the pty_insight_*audit* index to view the protection logs.

For more information about viewing the logs, refer to Working with Discover.

8.4 - Config.ini file for Application Protector

Sample config.ini file for Application Protector.

The Application Protector can be configured using the config.ini file. By default, this file is located in the <installation directory>/sdk/<protector>/data/ directory.

The various configurations required for setting up the Application Protector are described in this section.

Sample config.ini file

The following represents a sample config.ini file.

# -----------------------------
# Protector configuration
# ----------------------------- 
[protector]

# Cadence determines how often the protector connects with shared memory to fetch the policy updates in background.
# Default is 60 seconds. So by default, every 60 seconds protector tries to fetch the policy updates.
#
# Default 60.
cadence = 60

# The time during which an session object is valid. Default = 15 minutes.
session.sessiontimeout = 15

###############################################################################
# Log Provider Config
###############################################################################
[log]

# In case that connection to fluent-bit is lost, set how audits/logs are handled
# 
# drop  : (default) Protector throws logs away if connection to the fluentbit is lost
# error : Protector returns error without protecting/unprotecting 
#         data if connection to the fluentbit is lost
mode = drop

# Host/IP to fluent-bit where audits/logs will be forwarded from the protector
#
# Default localhost
host = localhost

Different configurations for Application Protector

The following are the various configurations:

Protector configurations

  • cadence: The interval at which the protector synchronizes with the shared memory for fetching the package with policy. The default value for cadence is 60 seconds. The maximum and minimum value that can be set for cadence are 86400 seconds (24 hours) and 0 respectively.
    For more information about the policy deployment with different cadence configurations, refer to Policy Deployment.
    For more information about the Resilient Package sync configuration parameters, refer to Resilient Package Sync Configuration Parameters.
    For more information about changing protector status interval, refer to Resilient Package Status Configuration Parameter.
  • session.sessiontimeout: The time during which a session object is valid. The default value for session.sessiontimeout is 15 minutes.

    Note: The session.sessiontimeout parameter is supported only by AP Java. Other Application Protectors do not support this parameter.

Log Provider configurations

  • mode: This describes how the protector logs are handled if you lose connection to the Log Forwarder host, can be set to one of the following values:
    • drop: The logs are dropped when the connection to the Log Forwarder is lost. The default mode is drop.
    • error: The data security operations are stopped and an error is generated when the connection to the Log Forwarder is lost.
  • host: The Log Forwarder hostname or IP address where the logs will be forwarded from the protector. The default host for Log Forwarder is localhost.

For more information about the configuration parameters for forwarding the audits and logs, refer to Configuration Parameters for Forwarding Audits and Logs.

8.5 - Multi-node Application Protector Architecture

Architecture for multi-node Application Protector.

The multi-node Application Protector (AP) architecture, its individual components, and how logs are collected using the Log Forwarder are described in this section.

The following figure describes the multi-node AP architecture.

Multi-node AP architecture

For example, some AP nodes are connected to an ESA, which includes the Audit Store component. Each AP node contains a Log Forwarder, RP Agent, and AP instance for sending logs to the ESA.

Protector: The AP can be configured using the config.ini file.
For more information about the configurations, refer to Config.ini file for Application Protector.

RP Agent: The RP Agent downloads the package with policy from the ESA, which is used by the protector to perform the protect, unprotect, or reprotect operations. It checks for the updates in the policy at set intervals and downloads the latest policy package when an update is detected.

Log Forwarder: The Log Forwarder component collects the logs from the AP and forwards them to the Audit Store. The Log Forwarder uses the 15780 port which is configurable to transport protection and audit logs to the ESA. The ESA receives the logs and stores it in the Audit Store.

8.6 - Uninstalling the Application Protector

Uninstalling the AP C Installation on different platforms

Uninstalling Application Protector (AP) C from Linux

This section outlines the steps to uninstall the various components of AP C from a Linux platform.

Uninstalling the Log Forwarder from Linux

Note: To preserve all the configurations while upgrading the Log Forwarder, ensure all the files present under the /opt/protegrity/logforwarder/data/config.d directory are backed up.

To uninstall the Log Forwarder from a Linux platform:

  1. Navigate to the /opt/protegrity/logforwarder/bin directory.

  2. Stop the Log Forwarder using the following command.

    ./logforwarderctrl stop
    
  3. Delete the /opt/protegrity/logforwarder directory.

    The Log Forwarder is uninstalled.

Uninstalling the RP Agent from Linux

Note: Before uninstalling the RP Agent, ensure that all the files present under the /opt/protegrity/rpagent/data directory are backed up.

To uninstall the RP Agent from a Linux platform:

  1. Navigate to the /opt/protegrity/rpagent/bin directory.

  2. Stop the RP Agent using the following command.

    ./rpagentctrl stop
    
  3. Delete the /opt/protegrity/rpagent directory.

    The RP Agent is uninstalled.

Uninstalling the AP C from Linux

To uninstall the AP C from a Linux platform:

  1. Navigate to the /opt/protegrity/sdk directory.

  2. Delete the /c directory.

    The AP C is uninstalled.