# Encrypting / Decrypting Request

All requests sent to the cNGN API are encrypted using AES encryption for request payloads and Ed25519 decryption for response data.

### Ed25519 Example:&#x20;

{% tabs %}
{% tab title="Typescript" %}
{% code overflow="wrap" %}

```typescript
import sodium from 'libsodium-wrappers';
import {Buffer} from 'buffer';

export class Ed25519Crypto {

    private static isInitialized = false;

    private static async initialize() {
        if (!this.isInitialized) {
            await sodium.ready;
            this.isInitialized = true;
        }
    }

    private static parseOpenSSHPrivateKey(privateKey: string): Uint8Array {
        const lines = privateKey.split('\n');
        const base64PrivateKey = lines.slice(1, -1).join('');
        const privateKeyBuffer = Buffer.from(base64PrivateKey, 'base64');

        const keyDataStart = privateKeyBuffer.indexOf(Buffer.from([0x00, 0x00, 0x00, 0x40]));
        if (keyDataStart === -1) {
            throw new Error('Unable to find Ed25519 key data');
        }

        return new Uint8Array(privateKeyBuffer.subarray(keyDataStart + 4, keyDataStart + 68));
    }

    public static async decryptWithPrivateKey(ed25519PrivateKey: string, encryptedData: string): Promise<string> {
        await this.initialize();

        try {
            const fullPrivateKey = this.parseOpenSSHPrivateKey(ed25519PrivateKey);
            const curve25519PrivateKey = sodium.crypto_sign_ed25519_sk_to_curve25519(fullPrivateKey);
            const encryptedBuffer = Buffer.from(encryptedData, 'base64');

            const nonce = encryptedBuffer.subarray(0, sodium.crypto_box_NONCEBYTES);
            const ephemeralPublicKey = encryptedBuffer.subarray(-sodium.crypto_box_PUBLICKEYBYTES);
            const ciphertext = encryptedBuffer.subarray(sodium.crypto_box_NONCEBYTES, -sodium.crypto_box_PUBLICKEYBYTES);

            const decrypted = sodium.crypto_box_open_easy(
                ciphertext,
                nonce,
                ephemeralPublicKey,
                curve25519PrivateKey
            );

            return sodium.to_string(decrypted)
        } catch (error) {
            throw new Error(`Failed to decrypt with the provided Ed25519 private key: ${error}`);
        }
    }

}
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code overflow="wrap" %}

```python
# You have to install pynacl, cryptography

from nacl.public import PrivateKey, PublicKey, Box
from nacl.encoding import Base64Encoder
from nacl.bindings import crypto_sign_ed25519_sk_to_curve25519
import base64
import re

class Ed25519Crypto:
    is_initialized = False

    @staticmethod
    def initialize():
        if not Ed25519Crypto.is_initialized:
            # No explicit initialization is required for PyNaCl, but we keep this as a safeguard.
            Ed25519Crypto.is_initialized = True

    @staticmethod
    def parse_openssh_private_key(private_key: str) -> bytes:
        """
        Parses an OpenSSH Ed25519 private key to extract the 32-byte private key.
        
        :param private_key: The OpenSSH private key string.
        :return: The 32-byte Ed25519 private key as bytes.
        """
        # Remove the key header/footer and decode the base64 content
        private_key_stripped = re.sub(r'-----.* PRIVATE KEY-----', '', private_key).strip()
        private_key_stripped = re.sub(r"\s+", '', private_key_stripped)
        private_key_buffer = base64.b64decode(private_key_stripped)

        # Look for Ed25519 key data (00 00 00 20)
        key_data_start = private_key_buffer.find(b'\x00\x00\x00\x40')
        if key_data_start == -1:
            raise Exception('Unable to find Ed25519 key data')

        # The key starts after 0x00 0x00 0x00 0x20 (32-byte key length marker)
        return private_key_buffer[key_data_start + 4: key_data_start + 68]

    @staticmethod
    def decrypt_with_private_key(ed25519_private_key: str, encrypted_data: str) -> str:
        """
        Decrypts data using an Ed25519 private key (converted to Curve25519).
        
        :param ed25519_private_key: The OpenSSH Ed25519 private key string.
        :param encrypted_data: The encrypted data in base64 format.
        :return: The decrypted plaintext as a string.
        """
        Ed25519Crypto.initialize()

        try:
            # Parse the OpenSSH private key format and extract the Ed25519 private key
            ed25519_private_key_bytes = Ed25519Crypto.parse_openssh_private_key(ed25519_private_key)

            # Convert Ed25519 private key to Curve25519 private key for use with Box
            curve25519_private_key_bytes = crypto_sign_ed25519_sk_to_curve25519(ed25519_private_key_bytes)
            private_key = PrivateKey(curve25519_private_key_bytes)

            # Decode the base64-encoded encrypted data
            encrypted_buffer = base64.b64decode(encrypted_data)

            # Extract nonce (24 bytes), ephemeral public key (32 bytes), and ciphertext
            nonce = encrypted_buffer[:24]
            ephemeral_public_key = PublicKey(encrypted_buffer[-32:])
            ciphertext = encrypted_buffer[24:-32]

            # Create a Box with the recipient's Curve25519 private key and the sender's ephemeral public key
            box = Box(private_key, ephemeral_public_key)

            # Decrypt the ciphertext
            decrypted = box.decrypt(ciphertext, nonce)

            return decrypted.decode('utf-8')

        except Exception as e:
            raise Exception("Failed to decrypt with the provided Ed25519 private key: " + str(e))

```

{% endcode %}
{% endtab %}

{% tab title="PHP" %}
{% code overflow="wrap" %}

```php
<?php

class Ed25519Crypto
{
    private static $isInitialized = false;

    private static function initialize()
    {
        if (!self::$isInitialized) {
            if (!extension_loaded('sodium')) {
                throw new Exception("The sodium extension is not loaded");
            }
            self::$isInitialized = true;
        }
    }

    private static function parseOpenSSHPrivateKey(string $privateKey): string
    {
        $lines = explode("\n", $privateKey);
        $base64PrivateKey = implode('', array_slice($lines, 1, -1));
        $privateKeyBuffer = base64_decode($base64PrivateKey);

        // Look for the Ed25519 key data
        $keyDataStart = strpos($privateKeyBuffer, pack('C*', 0x00, 0x00, 0x00, 0x40));
        if ($keyDataStart === false) {
            throw new Exception('Unable to find Ed25519 key data');
        }

        // Extract the key data
        return substr($privateKeyBuffer, $keyDataStart + 4, 64);
    }

    public static function decryptWithPrivateKey(string $ed25519PrivateKey, string $encryptedData): string
    {
        self::initialize();

        try {
            $fullPrivateKey = self::parseOpenSSHPrivateKey($ed25519PrivateKey);
            // Convert Ed25519 private key to Curve25519 private key
            $curve25519PrivateKey = sodium_crypto_sign_ed25519_sk_to_curve25519($fullPrivateKey);
            $encryptedBuffer = base64_decode($encryptedData);

            // Extract nonce, ephemeral public key, and ciphertext
            $nonce = substr($encryptedBuffer, 0, SODIUM_CRYPTO_BOX_NONCEBYTES);
            $ephemeralPublicKey = substr($encryptedBuffer, -SODIUM_CRYPTO_BOX_PUBLICKEYBYTES);
            $ciphertext = substr($encryptedBuffer, SODIUM_CRYPTO_BOX_NONCEBYTES, -SODIUM_CRYPTO_BOX_PUBLICKEYBYTES);

            $keyPair = sodium_crypto_box_keypair_from_secretkey_and_publickey($curve25519PrivateKey, $ephemeralPublicKey);

            // Decrypt the ciphertext
            $decrypted = sodium_crypto_box_open($ciphertext, $nonce, $keyPair);

            if ($decrypted === false) {
                throw new Exception('Decryption failed');
            }

            return $decrypted;
        } catch (Exception $e) {
            throw new Exception("Failed to decrypt with the provided Ed25519 private key: " . $e->getMessage());
        }
    }
}


```

{% endcode %}
{% endtab %}

{% tab title="Java" %}

```java
// Some code
```

{% endtab %}

{% tab title="Go" %}

```go
// Some code
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
// Some code
```

{% endtab %}
{% endtabs %}

### AES Example:

Example code that encrypts request data before sending  to the cNGN api

{% tabs %}
{% tab title="TypeScript" %}

```typescript
import * as crypto from 'crypto';

export type AESEncryptionResponse = {
    iv: string,
    content: string
}

export class AESCrypto {
    private static readonly ALGORITHM = 'aes-256-cbc';
    private static readonly IV_LENGTH = 16;
    private static readonly KEY_LENGTH = 32; // 256 bits

    private static prepareKey(key: string): Buffer {
        // Hash the key to ensure it's always the correct length
        const hash = crypto.createHash('sha256');
        hash.update(key);
        return hash.digest();
    }

    public static encrypt(data: string, key: string): AESEncryptionResponse {
        const iv = crypto.randomBytes(this.IV_LENGTH);
        const keyBuffer = this.prepareKey(key);

        const cipher = crypto.createCipheriv(this.ALGORITHM, keyBuffer, iv);

        let encrypted = cipher.update(data, 'utf8', 'base64');
        encrypted += cipher.final('base64');

        return {
            content: encrypted,
            iv: iv.toString('base64')
        };
    }

    public static decrypt(encryptedData: AESEncryptionResponse, key: string): string {
        const iv = Buffer.from(encryptedData.iv, 'base64');
        const keyBuffer = this.prepareKey(key);

        const decipher = crypto.createDecipheriv(this.ALGORITHM, keyBuffer, iv);

        let decrypted = decipher.update(encryptedData.content, 'base64', 'utf8');
        decrypted += decipher.final('utf8');

        return decrypted;
    }
}
```

{% endtab %}

{% tab title="Python" %}

```python
# You have to install cryptography

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import os
import base64

class AESCrypto:
    ALGORITHM = algorithms.AES
    IV_LENGTH = 16
    KEY_LENGTH = 32  # 256 bits

    @staticmethod
    def prepare_key(key: str) -> bytes:
        # Hash the key using SHA-256 to ensure it's always the correct length (32 bytes)
        digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
        digest.update(key.encode('utf-8'))
        return digest.finalize()

    @staticmethod
    def encrypt(data: str, key: str) -> dict:
        # Generate a random Initialization Vector (IV)
        iv = os.urandom(AESCrypto.IV_LENGTH)
        key_buffer = AESCrypto.prepare_key(key)

        # Create cipher and encrypt the data
        cipher = Cipher(AESCrypto.ALGORITHM(key_buffer), modes.CBC(iv), backend=default_backend())
        encryptor = cipher.encryptor()

        # Pad data to be multiple of 16 bytes (block size for AES)
        padding_length = 16 - (len(data) % 16)
        padded_data = data + chr(padding_length) * padding_length
        encrypted = encryptor.update(padded_data.encode('utf-8')) + encryptor.finalize()

        # Return the encrypted content and the IV (both base64 encoded)
        return {
            'content': base64.b64encode(encrypted).decode('utf-8'),
            'iv': base64.b64encode(iv).decode('utf-8')
        }

    @staticmethod
    def decrypt(encrypted_data: dict, key: str) -> str:
        # Decode the base64 encoded IV and content
        iv = base64.b64decode(encrypted_data['iv'])
        encrypted_content = base64.b64decode(encrypted_data['content'])
        key_buffer = AESCrypto.prepare_key(key)

        # Create cipher and decrypt the data
        cipher = Cipher(AESCrypto.ALGORITHM(key_buffer), modes.CBC(iv), backend=default_backend())
        decryptor = cipher.decryptor()

        decrypted = decryptor.update(encrypted_content) + decryptor.finalize()

        # Remove padding
        padding_length = decrypted[-1]
        decrypted = decrypted[:-padding_length]

        return decrypted.decode('utf-8')
```

{% endtab %}

{% tab title="PHP" %}
{% code overflow="wrap" %}

```php
<?php

class AESCrypto
{
    private const ALGORITHM = 'aes-256-cbc';
    private const IV_LENGTH = 16;
    private const KEY_LENGTH = 32; // 256 bits

    private static function prepareKey(string $key): string
    {
        // Hash the key to ensure it's always the correct length
        return hash('sha256', $key, true); // returns raw binary output
    }

    public static function encrypt(string $data, string $key): array
    {
        // Generate a random Initialization Vector (IV)
        $iv = openssl_random_pseudo_bytes(self::IV_LENGTH);
        $keyBuffer = self::prepareKey($key);

        // Encrypt the data using AES-256-CBC
        $encrypted = openssl_encrypt($data, self::ALGORITHM, $keyBuffer, OPENSSL_RAW_DATA, $iv);

        // Return the encrypted content and the IV (both base64 encoded)
        return [
            'content' => base64_encode($encrypted),
            'iv' => base64_encode($iv)
        ];
    }

    public static function decrypt(array $encryptedData, string $key): string
    {
        // Decode the base64 encoded IV and content
        $iv = base64_decode($encryptedData['iv']);
        $encryptedContent = base64_decode($encryptedData['content']);
        $keyBuffer = self::prepareKey($key);

        // Decrypt the data
        $decrypted = openssl_decrypt($encryptedContent, self::ALGORITHM, $keyBuffer, OPENSSL_RAW_DATA, $iv);

        return $decrypted;
    }
}

```

{% endcode %}
{% endtab %}

{% tab title="Java" %}

{% endtab %}

{% tab title="Go" %}

{% endtab %}

{% tab title="Ruby" %}

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.cngn.co/getting-started/security/encrypting-decrypting-request.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
