Silent Enrollment

Enroll users b2b through a single selfie.

Integration Guide

Enrolling a user with their picture is quite a straightforward process, but there are some security measures that need to be followed during implementation.

In order to perform an enrollment you must make a post request to the Web SDK server, with the following path:

/v1/idv-bridge/{customer}/{username}

The body of this request is going to be the image, which must be sent as binary.

This endpoint is behind authorization, which must be provided through the kl-api-key header, this key will be given to you by Keyless.

kl-api-key: API_KEY

The image must be sent encrypted, the encryption can be done with AES-GCM or AES-GCM-SIV, we recommend using AES-GCM-SIV if possible since it’s more secure.

Let’s explain the encryption flow in steps.

Create AES-GCM or AES-GCM-SIV Key

Generate a new AES-GCM or AES-GCM-SIV key on your server, length of the key can be 128, 192 or 256 bits. The important bit here is to have access to the raw bytes of the key.

import { siv } from '@noble/ciphers/aes'
import { getRandomValues } from 'crypto'

// fill a 128 bits Uint8Array with cryptographically secure random values
const key = getRandomValues(new Uint8Array(16))

// fill a 96 bits Uint8Array with cryptographically secure random values
const nonce = getRandomValues(new Uint8Array(12))

// create the AES-GCM-SIV cipher with key and nonce 
const cipher = siv(key, nonce)

Using @noble/ciphers in Node.js the nonce must be generated beforehand.

This key is required to symmetrically encrypt the image and transmit it safely over the internet, this is on top of TLS making it very hard for an attacker to access the image in clear.

Encrypt Image with AES-GCM or AES-GCM-SIV Key

Now that you have the AES-GCM or AES-GCM-SIV key ready to use, generate 96 random bits to use as the nonce and encrypt the image.

The nonce bytes must be prepended to the encrypted image bytes.

import { siv } from '@noble/ciphers/aes'
import { getRandomValues } from 'crypto'

// read real image here
const face = new Uint8Array()

const key = getRandomValues(new Uint8Array(16))
const nonce = getRandomValues(new Uint8Array(12))
const cipher = siv(key, nonce)

// encrypt the face Uint8Array
const encryptedFace = cipher.encrypt(face)

// create a new Uint8Array with enough bytes to contain both nonce and encrypted face bytes
const encryptedFaceWithNonce = new Uint8Array(nonce.length + encryptedFace.length)

// set the nonce bytes from the first position
encryptedFaceWithNonce.set(nonce, 0)

// set the encrypted face bytes after the nonce bytes
encryptedFaceWithNonce.set(encryptedFace, nonce.length)

Sending the nonce separately is not a best practice, so it must be prepended to the encrypted face bytes. This is a standard and will make it possible for the Web SDK server to decrypt it.

Encrypt AES-GCM or AES-GCM-SIV Key

In order for the Web SDK server to decrypt the image, the AES-GCM or AES-GCM-SIV key must be passed, but it will also need to be encrypted, this time with a RSAES-OAEP-SHA-256 public key.

The RSAES-OAEP-SHA-256 public key will be given to you in SPKI format by Keyless.

import { getRandomValues, publicEncrypt } from 'crypto'

const key = getRandomValues(new Uint8Array(16))

// put complete public key here
const publicKey = '-----BEGIN PUBLIC KEY-----...'

// encrypt the key
const encryptedKey = publicEncrypt(publicKey, key)

This marks the last bit of encryption that must be done before sending the request, let’s look at the headers that must be specified.

The key that is used to encrypt the image must be sent to the Web SDK server in order for it to decrypt the image again, but it cannot of course be sent in clear. This is why it must be encrypted first by leveraging RSA asymmetric encryption capabilities.

The Web SDK server has exclusive access to the RSA private key counterpart, meaning that it’s the only entity with the permissions to decrypt anything that is encrypted with the RSA public key.

Furthermore, the Web SDK server does not know the RSA private key at any point in time, decryption is handled by KMS.

Set Encryption Headers

kl-key-id: KEYLESS_KEY_ID
kl-key-algorithm: RSAES-OAEP-SHA-256
kl-image-key: ENCRYPTED_KEY_USED_TO_ENCRYPT_IMAGE
kl-image-algorithm: AES-GCM or AES-GCM-SIV
  • The kl-key-id will be provided by Keyless.

  • The kl-key-algorithm can only be RSAES-OAEP-SHA-256.

  • The kl-image-key is the encrypted key that you used to encrypt the image, please check the Encrypt AES-GCM or AES-GCM-SIV Key step, must be hex encoded.

  • The kl-image-algorithm can be either AES-GCM or AES-GCM-SIV depending on which one you used to encrypt the image.

Last updated