Implementation

Technical steps to implement the API

This is the ExeQuantum API for post quantum encryption. It uses ML-KEM for key encapsulation, as it was the NIST's choice for its post quantum security standardization competition.

It also utilizes its digital signature counterpart, ML-DSA, to digitally sign the key and verify its origin.

Unlike other algorithms like RSA, ML-KEM doesn't perform the encryption per se. Rather, it uses key encapsulation to generate a shared secret for symmetric encryption-decryption.

Example API Code

If you want to test the API for yourself before doing anything, feel free to clone https://github.com/samuelnsam/js-api-example and check it out. See for yourself how you can implement ML-KEM in 50 lines of code!

Example use cases for ML-KEM:

  • End to End Encryption

  • TLS

  • Symmetric Encryption

Example use cases for ML-DSA:

  • Message verification

  • Blockchain/Cryptocurrency proof of ownership

Below are the steps for ML-KEM and ML-DSA.

Before starting: Get your API token

PQCaaS uses an authentication token to allow calls into our API. When you sign up, you'll get a root token that does not expire (unless you reset it). It will appear in your dashboard, hidden until you choose to expose it, like so:

API dashboard

You can also copy it without showing it, if you need extra privacy.

It is highly recommended that you only use this token from secure environments, and only use temporary (ideally single use) tokens when making calls from more exposed environments like frontend. Steps to generate a temporary tokens will be described later in this documentation.

Below that, you'll see the dashboard that shows your API usage for the month. Keep an eye on it to track your usage.

Usage metric and cost

That's basically it for the dashboard. Now we can begin the integration steps. If you want to learn a bit more the high level overview of how ML-KEM and ML-DSA work, please refer to the Getting Started page.

In order to avoid overusing your sensitive authorization token, it's recommended to generate a temporary token that can be used for API calls.

You'll have to use the auth token for this call, so make sure to do it in a secure environment (i.e. backend)

This would be especially recommended when the API has to be called at areas where it has to be exposed to clients (i.e. frontend).

You can still use the auth token from your dashboard, at your own discretion.

You can determine how long the token should persist for (in seconds) through the expiry_time parameter. The default is 300 seconds or 5 minutes.

It's recommended to keep it relatively short (i.e. the average length of a session on your app) and handle switching as needed.

The token also has a limited number of times that it can get called. By default it's 1 attempt per token, and it's highly recommended

to keep it at 1, just to encapsulate the key in the frontend, but if you have to, you can set the number of retries allowed with the

parameter expire_after_attempts.

Note: examples are in Python, but the API can fit in every language.

Step 0:

import requests

response = requests.get(
    'https://api.exequantum.com/api/token/new',
    headers={'Authorization': 'bearer ' + AUTH_TOKEN}
)
temp_token = response.json()['new_token']
headers = {'Authorization': 'bearer ' + temp_token}

The procedure to create a shared secret for symmetric encryption-decryption purposes will be as follows:

ML-KEM - Step A (by person A):

headers = {'Authorization': 'Bearer ' + AUTH_TOKEN}

response = requests.get(
    'https://api.exequantum.com/api/kem/generate_keys',
    headers=headers
)
keys = response.json()

public_key = keys['pk']
secret_key = keys['sk']
verification_key = keys['verification_key']
signature = keys['signature']

This generates a key pair that gets sent over for encapsulation and decapsulation. The secret key gets encrypted before being sent over, and can only be used by us.

The verification key and signature are necessary for the ML-DSA signature on the public key to prevent tampering. The private signature key does not need to be shared for verification.

Person A will sent the public key, verification key and verification signature to person B, while keeping the secret key secured with themselves.

ML-KEM - Step B (by person B):

body = {
    'pk': public_key,
    'verification_key': verification_key,
    'signature': signature
}
response = requests.post(
    'https://api.exequantum.com/api/kem/encapsulate_key',
    headers=headers,
    json=body
)
enc = response.json()
cipher = enc['cipher']
shared_secret = enc['shared_key']

This API endpoint generates a shared secret that will be used for symmetric encryption. It should not be shared.

The cipher and the encrypted secret key can be sent back for the key decapsulation.

ML-KEM - Step C (by person A):

body = {
    'cipher': cipher,
    'sk': secret_key
}
response = requests.post(
    'https://api.exequantum.com/api/kem/decapsulate_key',
    headers=headers,
    json=body
)
dec = response.json()
shared_secret = dec['shared_key']

This API point generates the same shared secret. The cipher and secret keys are used for it.

Now that both sides of the communications have a secured shared secret, the secure communication can commence.

It is recommended to use AES256 for the encryption and decryption, as they're considered Quantum secure.

AES encryption and decryption are offered as API endpoints for free (Key generation and encapsulations are $1 per 4,000 requests), but you can feel free to use your own encryption.

If you decide to use our API for AES, it will go as follows:

ML-KEM - Step D (By encryptor):

body = {
    'unencrypted': 'hello world',
    'key': shared_secret
}
response = requests.post(
    'https://api.exequantum.com/api/aes/encrypt_text',
    headers=headers,
    json=body
)
encrypted_message = response.json()

This API call encrypted the message. Now to decrypt, the decryptor can just use the shared secret created by ML-KEM to decrypt the message.

ML-KEM - Step E (By decryptor):

body = {
    'ciphertext': encrypted_message,
    'key': shared_secret
}
response = requests.post(
    'https://api.exequantum.com/api/aes/decrypt_text',
    headers=headers,
    json=body
)
decrypted_message = response.json()

print("Decrypted message:", decrypted_message)

And that's it! You got your Quantum secure communication channel. If you want to learn more, feel free to book a demo.

Below are the steps to use ML-DSA

ML-DSA - Step A (By person A):

body = {
    'data': data
}
response = requests.post(
    'https://api.exequantum.com/api/signature/sign',
    headers=headers,
    json=body
)

data = response.json()

pk = data['pk']
sk = data['sk']
signature = data['signature']

From the endpoint, you get the generated signature and the public key that is used to verify the signature, as well as the secret key used to generate it.

The data, public key and signature should be sent over to person B, while person A needs to keep the secret key secure.

ML-DSA - Step B (By person B):

body = {
    'data': data,
    'pk': pk,
    'signature': signature
}
response = requests.post(
    'https://api.exequantum.com/api/signature/verify',
    headers=headers,
    json=body
)

data = response.json()

verified = data['verified']

The verified data will indicate whether the signature is valid. It returns false is the signature or data was tampered with and true if it's all good.

To see the terms and conditions of our API click here.

Generate Token

get

Parameters: expiry_time (optional), expire_after_attempts (optional)

Headers: Authorization token

Response: { 'new_token': string }

Documentation: This API endpoint generates a temporary token, that lasts a set amount of time and can be used for a set amount of attempts.

Query parameters
expiry_timeintegerOptionalDefault: 300
expire_after_attemptsintegerOptionalDefault: 1
Responses
200
Successful Response
application/json
Responseany
get
GET /api/token/new HTTP/1.1
Host: 
Accept: */*

No content

Generate Keys

get

Parameters: None

Headers: Authorization token

Response: { 'pk': string, 'verification_key': string }

Documentation: This API endpoint generates a key pair, a public key (pk) and a secret key (sk). It also generates a verification key and a signature for digital verification purposes. The public key can be sent over to the message sender for encapsulation. The secret key gets stored privately for later retrieval.

Responses
200
Successful Response
application/json
Responseany
get
GET /api/kem/generate_keys HTTP/1.1
Host: 
Accept: */*
200

Successful Response

No content

Encapsulate Key

post

Body: pk, signature, verif_key

Headers: Authorization token

Response: { 'cipher': string, 'shared_key': string }

Documentation: This API endpoint uses the public key to encapsulate the key, which generates a random cipher and a shared secret. The cipher can be sent back to the message sender to use for decapsulation.

Body
pkstringRequired
signaturestringRequired
verif_keystringRequired
Responses
200
Successful Response
application/json
Responseany
post
POST /api/kem/encapsulate_key HTTP/1.1
Host: 
Content-Type: application/json
Accept: */*
Content-Length: 51

{
  "pk": "text",
  "signature": "text",
  "verif_key": "text"
}

No content

Decapsulate Key

post

Parameters: cipher, pk

Headers: Authorization token

Response: { 'shared_key': string }

Documentation: This API endpoint uses the the random cipher to generate a shared secret. If done correctly, this shared secret will now be only in the possession of the message sender and receiver and can be used as a symmetrical key to encrypt and decrypt any further data communication.

Body
cipherstringRequired
skstringRequired
Responses
200
Successful Response
application/json
Responseany
post
POST /api/kem/decapsulate_key HTTP/1.1
Host: 
Content-Type: application/json
Accept: */*
Content-Length: 29

{
  "cipher": "text",
  "sk": "text"
}

No content

Sign

post

Body: data

Headers: Authorization token

Response: { 'signature': string, 'pk': string }

Documentation: This API endpoint generates a digital signature on the data.

Body
datastringRequired
Responses
200
Successful Response
application/json
Responseany
post
POST /api/signature/sign HTTP/1.1
Host: 
Content-Type: application/json
Accept: */*
Content-Length: 15

{
  "data": "text"
}

No content

Verify

post

body: data, pk, signature

Headers: Authorization token

Response: { 'verified': boolean }

Documentation: This API endpoint uses the signature and a public key to verify the origin of the data

Body
datastringRequired
pkstringRequired
signaturestringRequired
Responses
200
Successful Response
application/json
Responseany
post
POST /api/signature/verify HTTP/1.1
Host: 
Content-Type: application/json
Accept: */*
Content-Length: 46

{
  "data": "text",
  "pk": "text",
  "signature": "text"
}

No content

Encrypt Text

post

Body: unencrypted

Headers: Authorization token

Response: Encrypted message text

Documentation: This API endpoint uses the shared key to perform an AES encryption on a string (can be base64 or hex).

Body
unencryptedstringRequired
keystringRequired
Responses
200
Successful Response
application/json
Responseany
post
POST /api/aes/encrypt_text HTTP/1.1
Host: 
Content-Type: application/json
Accept: */*
Content-Length: 35

{
  "unencrypted": "text",
  "key": "text"
}

No content

Decrypt Text

post

Body: key, ciphertext

Headers: Authorization token

Response: Decrypted message text

Documentation: This API endpoint uses the shared key to perform an AES decryption on a fstringile.

Body
ciphertextstringRequired
keystringRequired
Responses
200
Successful Response
application/json
Responseany
post
POST /api/aes/decrypt_text HTTP/1.1
Host: 
Content-Type: application/json
Accept: */*
Content-Length: 34

{
  "ciphertext": "text",
  "key": "text"
}

No content

Last updated