---
title: "Plugins – Lua Programming Reference"
slug: "fortanix-dsm-plugins-lua-programming-reference"
updated: 2026-03-10T17:16:58Z
published: 2026-03-10T17:16:58Z
canonical: "support.fortanix.com/fortanix-dsm-plugins-lua-programming-reference"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://support.fortanix.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Plugins – Lua Programming Reference

## 1.0 Language

Lua plugins are run using the standard Lua 5.3 interpreter.

Plugins may use the following standard Lua 5.3 symbols:

- assert
- bit32
- collectgarbage
- coroutine
- error
- getmetatable
- ipairs
- load
- math
- next
- pairs
- rawequal
- rawget
- rawlen
- rawset
- select
- setmetatable
- string
- table
- tonumber
- tostring
- type
- utf8
- xpcall
- _VERSION
- _G

The following will be supported soon:

- print
- require

Support is not planned for the following:

- loadfile
- arg
- debug
- dofile
- package
- os
- io

The following **Fortanix-Data-Security-Manager (DSM)**-defined symbols are also in scope, detailed below:

- cbor
- json
- Error
- Blob
- EntityId
- Sobject
- App
- User
- Plugin
- Group
- AuditLog
- principal
- this_plugin
- digest
- null
- BigNum
- Time
- EcGroup
- EcPoint
- require_approval_for

## 2.0 Invoking Plugins

### 2.1 The `run` Function

A plugin must define a function `run`.

When the plugin is invoked, `run` will be called with the following parameters:

- `input`: The JSON data with which the plugin was invoked, decoded into a Lua object as in `json.decode`, or `nil` if no input data was supplied.
- `url`: The URL that was used to invoke the plugin, represented as a Lua table:
  - `url.path`: A Lua array of path components. For example, `['sys', 'v1', 'plugins', '08ce8c3e-50fc-4c95-a633-ca417e6f2828']`. Users and apps may append arbitrary custom path components when invoking a plugin.
  - `url.query`: The query string (the part of the URL after `?`), or `nil` if the URL does not include a query string.
- `method`: The method used to invoke the plugin, either `'POST'` or `'GET'`.

The function `run` may accept any prefix of these arguments. For example,

- `function run() ... end`
- `function run(input) ... end`
- `function run(input, url, method) ... end`

The function `run` may return the following:

- `nil`: the plugin invocation returns successfully with no content.
- an encodable Lua object: the object is encoded to JSON as in `json.encode` and returned.
- `nil` and an `Error`: the plugin invocation errors as described in the `Error` section below.

### 2.2 The `check` Function

A plugin can optionally define a function `check`.

If this function is defined, it will be called before function `run` and with the same input parameters that will be passed to function `run`. For example, `function check(input, url, method) ... end`.

If the `check` function returns an error or throws an exception, the plugin invocation is aborted and the error will be returned to the caller.

You can use function `check` for access checks, input validation logic, and so on. You can also call `require_approval_for` in function `check` to require the caller to submit an approval request to invoke the plugin subject to the approval policy defined on the object you pass to `require_approval_for`. For example,

```bash
function check(input)
   -- Validate input fields
   if not input.signing_key_id then return nil, 'missing field `signing_key_id`' end
   if not input.hash then return nil, 'missing field `hash`' end
   -- Look up the signing key
   local signing_key = assert(Sobject { kid = input.signing_key_id })
   -- If the signing key is subject to an approval policy, then the following
   -- call will ensure that this plugin can only be invoked by creating an
   -- approval request. Otherwise the call has no effect.
   require_approval_for(signing_key)
end

function run(input)
  local signing_key = assert(Sobject { kid = input.signing_key_id })
  local signature =assert(signing_key:sign { hash = input.hash, hash_alg = 'SHA256' })
  return signature
end
```

Note that even though you can call `require_approval_for` in function `run`, it will not have any effect. You should only use it in function `check`.

### 2.3 The `Blob` Class

A `Blob` represents arbitrary binary data.

DSM REST APIs use JSON and distinguish between

- UTF-8 strings (e.g. key name), which use JSON strings, and
- binary data (e.g. key material), which use base64-encoded JSON strings.

JSON strings must be valid UTF-8, so in general raw binary data is not a valid JSON string.

Lua plugin APIs use Lua strings for UTF-8 strings and `Blob`s for binary data.

For interoperability with the JSON REST APIs, a base64-encoded Lua string may be used instead of a `Blob` where binary data is expected.

**Constructors**

- `Blob.from_bytes(string)`

Constructs a `Blob` from a Lua string with raw binary data.

Note that Lua strings do not need to be valid UTF-8.

For example, `local blob = Blob.from_bytes('\1\2\255')`.
- `Blob.from_base64(string)`

Constructs a `Blob` from base64-encoded data in a Lua string.

For example, `local blob = Blob.from_base64('AQL/')`.
- `Blob.from_hex(string)`

Constructs a `Blob` from hex-encoded data in a Lua string.

For example, `local blob = Blob.from_hex('0102FF')`.
- `Blob.from_base58(string)`

Constructs a `Blob` from base58-encoded data in a Lua string.

For example, `local blob = Blob.from_base58('LiA')`.
- `Blob.from_base58_check(string)`

Constructs a `Blob` from base58-encoded data in a Lua string. Unlike plain base58, this also includes a SHA-256d checksum.

For example, `local blob = Blob.from_base58_check('3DyWSpcNi')`.
- `Blob.random(num_bytes)`

Constructs a random `Blob` with the given number of bytes.

The bytes will be generated using DSM's cryptographically secure random number generator.

`Blob.random { bytes = num_bytes }` and `Blob.random { bits = num_bits }` are also supported.

`num_bytes` must be a whole number; `num_bits` must be a multiple of 8.
- `Blob.pack(fmt, ...)`

This is equivalent to `Blob.from_bytes(string.pack(fmt, ...))`.

Refer to the documentation for `string.pack`.

For example, `assert(Blob.pack('&gt;i8', 0xDEADBEEF):hex() == '00000000DEADBEEF')`

**Methods**

- `blob:unpack(fmt)`

This is equivalent to `string.unpack(fmt, blob:bytes())`.

Refer to the documentation for `string.unpack`.

For example, `assert(Blob.from_hex('00000000DEADBEEF'):unpack('&gt;i8') == 0xDEADBEEF)`
- `blob:bytes()`

Returns a Lua string with the raw binary data.

For example, `assert(blob:bytes() == '\1\2\255')`
- `blob:base64()`

Returns a Lua string with the base64-encoded binary data.

For example, `assert(blob:base64() == 'AQL/')`
- `blob:hex()`

Returns a Lua string with the hex-encoded binary data.

For example, `assert(blob:hex() == '0102FF')`
- `blob:base58()`

Returns a Lua string with the base58-encoded binary data.

E.g.: `assert(blob:base58() == 'LiA')`
- `blob:base58_check()`

Returns a Lua string with the base58-encoding (including SHA-256d checksum) of the binary data.

For example, `assert(blob:base58_check() == '3DyWSpcNi')`
- `blob:slice(lo_byte, hi_byte)`

Returns a blob consisting of the bytes between `lo_byte` and `hi_byte`, 1-indexed and inclusive.

For example, `Blob.from_bytes('\1\2\3\4'):slice(2, 3) == Blob.from_bytes('\2\3')`.
- `blob .. blob2`

`..` may be used to concatenate two blobs, e.g. `Blob.from_bytes('\1') .. Blob.from_bytes('\2') == Blob.from_bytes('\1\2')`.
- `#blob`

`#` may be used to get the number of bytes in the blob, e.g. `assert(#Blob.from_bytes('\1\1\1') == 3)`.
- `blob &amp; blob2`, `blob | blob2`, `blob ~ blob2`

`&amp;`, `|`, and `~` may be used to take the bitwise and, the bitwise or, and the bitwise xor of two blobs. The blobs must be the same size, or else these operations will throw an exception.
- `~blob`

`~` may be used to take the bitwise not of a blob.

### 2.4 The `cbor` Module

- `cbor.encode(object)`

`cbor.encode` encodes (serializes) a Lua object into a `Blob` containing binary data in the CBOR format.

Encodable Lua objects include:

A Lua object may use a custom encoder by adding an encoder function `__tocbor` to the metatable.

There is no corresponding way to use a custom decoder.

For example,

```bash
null = setmetatable({}, { __tocbor = function(self) return cbor.encode(nil) end })
```
  - `nil`, which encodes to null.
  - Lua numbers and booleans, which encode to CBOR numbers and booleans.
  - `Blob`s, which encode to CBOR byte strings.
  - Lua strings, which encode to CBOR text strings.
    - CBOR text string should be valid UTF-8, while Lua strings may contain arbitrary binary data. To encode a Lua string representing potentially non-UTF-8 binary data, use `Blob.from_bytes(string)`.
  - Lua tables, which encode to CBOR maps or CBOR array.
    - A table is encoded as an array if the set of keys is `{1, 2, .., n}` for some `n`.
- `cbor.decode(data)`

`cbor.decode` decodes (deserializes) a blob containing CBOR binary data, returning a Lua object that encodes to the given CBOR as defined in `cbor.encode`.

CBOR byte strings decode to `Blob`s and CBOR nil decodes to `null`.

The other CBOR types decode to primitive strings, numbers, booleans, and tables.

### 2.5 The `json` Module

- `json.encode(object)`

`json.encode` encodes (serializes) a Lua object into Lua string containing JSON data.

`json.encode(object)` returns the JSON corresponding to `cbor.encode(object)`.

CBOR byte strings (`Blob`s) become base64-encoded JSON strings.
- `json.decode(data)`

`json.decode` decodes (deserializes) a Lua string containing JSON data, returning a Lua object that encodes to the given JSON, as defined in `json.encode`.

JSON nil decodes to `null`. The other JSON types decode to primitive strings, numbers, booleans, and tables.

JSON strings always decode to Lua strings, including base64-encoded JSON strings.

For example, encoding a `Blob` to JSON and then decoding the JSON will produce a base64-encoded Lua string. To get the original `Blob`, apply `Blob.from_base64` to the decoded string.

### 2.6 The `null` Object

This is an object that encodes to CBOR or JSON `null`.

This is returned on success in methods like `sobject:delete()` that return no data.

We do not return `nil` here so that `assert(sobject:delete())` works.

### 2.7 The `Array` Class

An `Array` is a table that always serializes to an JSON/CBOR array, not a map.

Non-numeric keys in an `Array` (more precisely, keys not included in `ipairs`) are ignored during serialization.

**Motivation**

The empty map `{}` and the empty array `[]` in JSON both deserialize to the empty Lua table `{}`.

The empty Lua table serializes to the empty map. `Array()` serializes to the empty array.

**Constructors**

`Array()` or `Array {}` is the empty array.

`Array(table)` returns `setmetatable(table, Array)`.

E.g. `assert(json.encode(Array { 1, 2, a = 3 }) == '[1,2]')`.

### 2.8 The `Error` Class

This class is used for errors from the plugin APIs.

If an error occurs, a function will return two values: `nil` and the error (this is a standard Lua idiom).

An error has two properties: an HTTP status code number `status` and `message` string.

Custom errors may be constructed using `Error.new { status = , message = "" }.`

For example,

```bash
function run()
   local result, error = Sobject { name = "nonexistent key" }
   assert(result == nil)
   assert(error.status == 404)

  // return a custom 401 Unauthorized error to the plugin invoker
  return nil, Error.new { status = 401, message = "custom error" }
end
```

### 2.9 The Function `principal`

`principal()` returns the `EntityId` of the entity that invoked the plugin.

For example,

```bash
function run()
  if principal():type() ~= 'user'
    return nil, Error.new { = 401, message = "Plugin may only be invoked by users" }
  end
end
```

### 2.10 The Function `this_plugin`

`this_plugin()` returns a `Plugin` object corresponding to the plugin being invoked.

For example,

```bash
function run()
  return "the name of this plugin is " .. this_plugin().name
end
```

### 2.11 The `AuditLog` Module

- `AuditLog.log { message = message, severity = severity }`

Write an audit log entry associated with this plugin with the given message and severity.

`severity` may be one of `'INFO'`, `'WARNING'`, `'ERROR'`, or `'CRITICAL'`.
- `AuditLog.get_all { ... }`

Get audit log entries matching the requested filters. This corresponds to `GET /sys/v1/logs` in the REST API.

Returns an array of audit log entries, or `nil` and an `Error` if the logs could not be fetched.

### 2.12 The `Sobject` Class

This represents a security object, transient or persisted.

The properties of this object are described in the REST API docs (it is named `KeyObject` there). `sobject.value` and sobject.pub_key are `Blobs` (unless they are nil).

`sobject.creator` is an `EntityId` object.

Like apps, plugin may create transient keys by adding `transient = true` to the create, import, unwrap, derive, or agree request.

These transient keys only exist during the invocation of the plugin. As soon as the plugin's `function run` returns, they become invalid.

**Constructors**

- `Sobject { id = '&lt;uuid&gt;' }` or `Sobject { kid = '&lt;uuid&gt;' }`

This returns the persisted security object with the given UUID, or `nil` and an `Error` if no such object exists.

For example,

```bash
local sobject = assert(Sobject { id = '123e4567-e89b-12d3-a456-426655440000' })
```
- `Sobject { name = 'key name' }`

This returns the persisted security object with the given name, or `nil` and an `Error` if no such object exists.
- `Sobject.create { ... }`

Create a security object. This corresponds to `POST /crypto/v1/keys` in the REST API.

The arguments to this function are described in the REST API documentation for `SobjectRequest`.

This returns the created `Sobject`, or `nil` and an `Error` if the object could not be created.

For example,

```bash
local sobject = assert(Sobject.create { name = "my key", obj_type = "AES", key_size = 128 })
```
- `Sobject.import { ... }`

Import a security object. This corresponds to `PUT /crypto/v1/keys` in the REST API.

The arguments to this function are described in the REST API documentation for `SobjectRequest`.

This returns the import `Sobject`, or `nil` and an `Error` if the object could not be imported.
- For example,

```bash
local sobject = assert(Sobject.import { name = "my key", obj_type = "AES", value = Blob.random { bits = 128 } })
```
- `Sobject.get_all { ... }`

Get detailed information on all security objects. This corresponds to `GET /crypto/v1/keys` in the REST API.

This returns `Sobject`, or `nil` and an `Error`.

For example,

```bash
local sobject, error = Sobject.get_all { }
```
- `Sobject.get_by_names { ... }`

Get detailed information on security objects that match names.

This returns `Sobject`, or `nil` and an `Error`.

For example,

```bash
local sobject, error = Sobject.get_by_names { "My key", "Another key", "Unavailable key" }
```

**Methods**

- `sobject:update { ... }`

Update the properties of `sobject`. This corresponds to `PATCH /crypto/v1/keys/` in the REST API.

The arguments to this method are described in the REST API documentation for `SobjectRequest`.

This method may not be called on a transient `sobject`.

Returns `null` on success, or `nil` and an `Error` if the object could not be updated.

For example,

```bash
assert(sobject:update { name = "new key name" }) -- throw an exception if update fails
```
- `sobject:delete()`

Delete the `sobject`. This corresponds to `DELETE /crypto/v1/keys/` in the REST API.

This method may not be called on a transient `sobject`.

Returns `null` on success, or `nil` and an `Error` if the object could not be deleted.

For example,

```bash
assert(sobject:delete()) -- throw an exception if update fails
```
- `sobject:export()`

Retrieve the value of `sobject`. This corresponds to `GET /crypto/v1/keys/export` in the REST API.

This returns a `Sobject` which has a `value` property on success, or returns `nil` and an `Error` if the object could not be exported.

For example,

```bash
local exported_value = assert(Sobject { name = 'my key' }):export().value
```
- `sobject:descriptor()`

Returns `{ kid = "&lt;uuid&gt;" }` if `sobject` is persisted, or `{ transient_key = blob }` if `sobject` is transient.
- `sobject:encrypt { ... }`

Encrypt data using `sobject`. This corresponds to `POST /crypto/v1/encrypt` in the REST API.

The arguments to this method are described in the REST API docs for `EncryptRequest`.

Returns an object corresponding to `EncryptResponse` in the REST API on success, or `nil` and an `Error` if the data could not be encrypted.

In the returned object, `cipher` and `iv` are `Blob`s.

The following Lua plugin demonstrates the encrypt operation using a symmetric key (AES). However, you can also perform the operation with an asymmetric key, such as RSA or EC.

For example,

Plugin input:

```json
plugin_input:
{
  "key_name" : "aes01Test",
  "plain_text" : "hello world",
  "mode" : "CBC"
}
```

Plugin code:

```lua
function run(input)
  local sobject = assert(Sobject { name = input.key_name })
  local encrypt_response = assert(sobject:encrypt { plain = Blob.from_bytes(input.plain_text), mode = input.mode })
  return { cipher = encrypt_response.cipher:base64(), iv = encrypt_response.iv:base64() }
end
```

Plugin output:

```json
{
  "cipher": "b/GWYnVVmrGxqPBenGCrlA==",
  "iv": "XoYrAjkoke9bf9/A1/Cq4A=="
}
```
- `sobject:decrypt { ... }`

Decrypt data using `sobject`. This corresponds to `the POST /crypto/v1/decrypt` endpoint in the REST API.

The arguments to this method are described in the REST API docs for `DecryptRequest`.

Returns an object corresponding to `DecryptResponse` in the REST API on success, or `nil` and an `Error` if the data could not be decrypted.

In the returned object, `plain` is a `Blob`.

The following Lua plugin demonstrates the decrypt operation using a symmetric key (AES). However, you can also perform the operation with an asymmetric key, such as RSA or EC.

For example,

Plugin input:

```json
plugin_input:
{
  "key_name" : "aes01Test",
  "cipher" : "b/GWYnVVmrGxqPBenGCrlA==",
  "mode" : "CBC",
  "iv" : "XoYrAjkoke9bf9/A1/Cq4A=="
}
```

Plugin code:

```lua
function run(input)
  local sobject = assert(Sobject { name = input.key_name })
  local encrypt_response = assert(sobject:encrypt { plain = Blob.from_bytes(input.plain_text), mode = input.mode })
  return { cipher = encrypt_response.cipher:base64(), iv = encrypt_response.iv:base64() }
end
```

Plugin output:

```json
[
  "hello world"
]
```
- `sobject:batch_encrypt { ... }` and `sobject:batch_decrypt { ... }`

`batch_encrypt`: Use this method to perform batch encrypt with one or more keys. The order of batch items in the response matches that of the request, and an individual status code is returned for each batch item. This corresponds to the `POST /crypto/v1/keys/batch/encrypt` endpoint in the [REST API](https://support.fortanix.com/apidocs/batch-encrypt-with-one-or-more-keys).

`batch_decrypt`: Use this method to perform batch decrypt with one or more keys. The order of batch items in the response matches that of the request, and an individual status code is returned for each batch item. This corresponds to the `POST /crypto/v1/keys/batch/decrypt` endpoint in the [REST API](https://support.fortanix.com/apidocs/batch-decrypt-with-one-or-more-keys).

The following Lua plugin demonstrates multiple encrypt and decrypt operations using a single AES key (though multiple keys are also supported). These operations are performed as single `batch-encrypt` and `batch-decrypt` requests, respectively.

For example:

Plugin input:

```json
plugin_input:
{
  "key_name" : "aes01Test",
  "alg" : "AES",
  "mode" : "CBC",
  "plain_text1" : "hello world",
  "plain_text2" : "loren ipsum"
}
```

Plugin code:

```lua
function run(input)
    local sobject = assert(Sobject { name = input.key_name })
    
    -- perform batch encrypt for the input plain_texts
    local plain1 = input.plain_text1
    local enc_body1 = { alg = input.alg, plain = Blob.from_bytes(plain1), mode = input.mode }
    local enc_req1 = { kid = sobject.kid, request = enc_body1 }
    
    local plain2 = input.plain_text2      
    local enc_body2 = { alg = input.alg, plain = Blob.from_bytes(plain2), mode = input.mode }
    local enc_req2 = { kid = sobject.kid, request = enc_body2 }
    
    local batch_enc_req = {}
    table.insert(batch_enc_req, enc_req1)
    table.insert(batch_enc_req, enc_req2)
    
    local batch_enc_response, err = batch_encrypt(batch_enc_req)
    
    -- check batch encrypt request is successful
    assert(err == nil)
    assert(batch_enc_response[1]["status"] == 200)
    assert(batch_enc_response[2]["status"] == 200)
    
    -- perform batch decrypt for the cipher_texts obtained above
    local cipher1 = batch_enc_response[1]["body"]["cipher"]
    local iv1 = batch_enc_response[1]["body"]["iv"]
    local dec_body1 = { alg = input.alg, mode = input.mode, cipher = cipher1, iv = iv1 }
    local decr_req1 = { kid = sobject.kid, request = dec_body1 }
    
    local cipher2 = batch_enc_response[2]["body"]["cipher"]
    local iv2 = batch_enc_response[2]["body"]["iv"]
    local dec_body2 = { alg = input.alg, mode = input.mode, cipher = cipher2, iv = iv2 }
    local decr_req2 = { kid = sobject.kid, request = dec_body2 }
    
    local batch_decr_req = {}
    table.insert(batch_decr_req, decr_req1)
    table.insert(batch_decr_req, decr_req2)
    
    local batch_decr_response, err = batch_decrypt(batch_decr_req)
    
    -- check batch decrypt request is successful
    assert(err == nil)
    assert(batch_decr_response[1]["status"] == 200)
    assert(batch_decr_response[2]["status"] == 200)
    
    -- check decrypted cipher match their corresponding input plain_texts
    assert(plain1 == batch_decr_response[1]["body"]["plain"]:bytes())
    assert(plain2 == batch_decr_response[2]["body"]["plain"]:bytes())
    
    -- return the decrypted texts
    return { batch_decr_response[1]["body"]["plain"]:bytes(), batch_decr_response[2]["body"]["plain"]:bytes() }
end
```

Plugin output:

```json
[
  "hello world",
  "loren ipsum"
]
```
- `sobject:sign { ... }`

Sign data using `sobject`. This corresponds to `POST /crypto/v1/sign` in the REST API.

The arguments to this method are described in the REST API docs for `SignRequest`.

Returns a object corresponding to `SignResponse` in the REST API on success, or `nil` and an `Error` if the data could not be signed.

In the returned object, `signature` is a `Blob`.

For example,

```bash
local sobject = assert(Sobject { name = "my rsa key" })
local sign_response = assert(sobject:sign { data = Blob.from_bytes("hello world"), hash_alg = 'SHA256' })
return sign_response.signature
```
- `sobject:verify { ... }`

Verify data signed by `sobject`. This corresponds to `POST /crypto/v1/verify` in the REST API.

The arguments to this method are described in the REST API docs for `VerifyRequest`.

Returns a object corresponding to `VerifyResponse` in the REST API on success, or `nil` and an `Error` if the validity of the signature could not be established.

Note that if the signature is determined to be invalid, the call will successfully return `{ result = false }`.

For example,

```bash
local sobject = assert(Sobject { name = "my rsa key" })
local verify_response = assert(sobject:verify { data = Blob.from_bytes("hello world"), signature = signature, hash_alg = 'SHA256' })
assert(verify_response.result == true)
```
- `sobject:wrap { ... }`

Use `sobject` to wrap another security object. This corresponds to `POST /crypto/v1/wrapkey` in the REST API.

The arguments to this method are described in the REST API docs for `WrapKeyRequestEx`.

The `subject` argument may be a descriptor (as described in the REST API) or a `Sobject`.

Returns an object corresponding to `WrapKeyResponse` in the REST API on success, or `nil` and an `Error` if the sobject key could not be wrapped.

For example,

```bash
local wrapping_key = assert(Sobject { name = "AES wrapping key" })
local generated_key = assert(Sobject.create { obj_type = 'AES', key_size = 128, transient = true })
local wrap_response = assert(wrapping_key:wrap { subject = generated_key, mode = 'CBC' })
local result = wrap_response.wrapped_key:base64() .. ':' .. wrap_response.iv:base64()
```
- `sobject:unwrap { ... }`

Use `sobject` to unwrap and import a wrapped key. This corresponds to `POST /crypto/v1/unwrapkey` in the REST API.

The arguments to this method are described in the REST API docs for `UnwrapKeyRequest`.

Returns the unwrapped `Sobject`, or `nil` and an `Error` if the wrapped key could not be unwrapped.

For example,

```bash
function run(input)
  local wrapping_key = assert(Sobject { name = "RSA wrapping key" })
  local unwrapped_key =
    assert(wrapping_key:unwrap { wrapped_key = input.wrapped_key, obj_type = 'AES', transient = true })
  return unwrapped_key:encrypt(input.encrypt_request)
end
```
- `sobject:agree { ... }`

Perform a key agreement algorithm using the private key `sobject` and the public key from another party. This corresponds to `POST /crypto/v1/agree` in the REST API.

The arguments to this method are described in the REST API for `AgreeKeyRequest`.

The `public_key` argument may be a descriptor (as described in the REST API) or a `Sobject`.

Returns the agreed `Sobject`, or `nil` and an `Error` if the key agreement could not be completed.
- `sobject:mac { ... }`

Use `sobject` to compute a cryptographic Message Authentication Code on a message. `sobject` must be a symmetric key. This corresponds to `POST /crypto/v1/mac` in the REST API.

The arguments to this method are described in the REST API docs for `MacGenerateRequest`.

Returns an object corresponding to `MacGenerateResponse` from the REST API on success, or `nil` and an `Error` if the MAC could not be generated.
- `sobject:mac_verify { ... }`

Use `sobject` to verify a cryptographic Message Authentication Code on a message. `sobject` must be a symmetric key. This corresponds to `POST /crypto/v1/macverify` in the REST API.

The arguments to this method are described in the REST API docs for `MacVerifyRequest`.

Returns an object corresponding to `MacVerifyResponse` from the REST API on success, or `nil` and an `Error` if the validity of the MAC could not be established.

Note that if the MAC is determined to be invalid, the call will successfully return `{ result = false }`.
- `sobject:derive { ... }`

Use `sobject` to derive another key. This corresponds to `POST /crypto/v1/derive`in the REST API.

The arguments to this method are described in the REST API docs for `DeriveKeyRequest`.

Returns a newly created `Sobject`, or `nil` and an `Error` if the key could not be derived.

For example,

```bash
function run(input)
    local sobject = assert(Sobject.create { name = "test_key_20230314", obj_type = "AES", key_size = 256 })
    local derive_response = assert(sobject:derive { name = "derived_key_20230314", 
                                                    key_size = 256, key_type = "AES", 
                                                    mechanism = { encrypt_data = { alg = "AES", mode = "CBC", iv = "6pduXJMk351eaTbb0XLozw==",
                                                                  plain = "OjKlKPBH89/K8QfdxiutVyD/yAMg0k+NH7xeFEsR0hxkUwOaen+Uv/uXCvPUNj53"}}})
     return derive_response
 end
```
- `sobject:agree { ... }`
- `Sobject:issuer_dn()`

If the object is a certificate, then returns an `X509Name` representing the certificate issuer.
- `Sobject:subject_dn()`

If the object is a certificate, then returns an `X509Name` representing the certificate subject.
- `Sobject:get_extension(oid)`

If the object is a certificate, then returns the value of the extension specified by the `OID` argument.

For example, `let extn = sobject.get_extension(Oid.from_str('subjectKeyIdentifier'))`
- `Sobject:extension_oids()`

If the object is a certificate, returns a list of `OID`s which are used in the certificate.

For example, `let extn_oids = sobject.extension_oids()`
- `Sobject:verify_certificate(issuing_cert)`

If the object is a certificate, then verifies the signature on the cert using the issuing certificate which is provided as an argument.

### 2.13 The `EntityId` Class

This identifies an app, user, or plugin. It corresponds to `CreatorType` in the REST API docs.

**Methods**

- `entity_id:id()`

Returns the UUID of the app, user, or plugin.
- `entity_id:type()`

Returns `"app"`, `"user"`, or `"plugin"`.
- `entity_id:entity()`

Returns the appropriate `App`, `User`, or `Plugin` object, or `nil` and an `Error` if no such object exists.

For example,

```bash
local key = assert(Sobject { name = "my key" })
local creator = key.creator
return "Created by a " .. creator:type() .. " named " .. creator:entity().name
```

### 2.14 The `App` Class

This represents an app.

The properties of this object are described in the REST API docs.

`app.creator` is an `EntityId` object.

**Constructors**

- `App { id = "&lt;uuid&gt;" }`

This returns the app with the given UUID, or `nil` and an `Error` if no such app exists.

### 2.15 The `Plugin` Class

This represents a Plugin.

The properties of this object are described in the REST API docs.

`plugin.creator` is an `EntityId` object.

**Constructors**

- `Plugin { id = "&lt;uuid&gt;" }`

This returns the plugin with the given UUID, or `nil` and an `Error` if no such object exists.

### 2.16 The `User` Class

This represents a User.

The properties of this object are described in the REST API docs.

**Constructors**

- `User { id = "&lt;uuid&gt;" }`

This returns the user with the given UUID, or `nil` and an `Error` if no such user exists.

### 2.17 The `Group` Class

This represents a Group.

The properties of this object are described in the REST API docs.

**Constructors**

- `Group { id = "&lt;uuid&gt;" }`

This returns the group with the given UUID, or `nil` and an `Error` if no such group exists.

### 2.18 The Function `digest`

Compute the digest (hash) of the given data using the given algorithm. This corresponds to `POST /crypto/v1/digest` from the REST API.

`digest` returns an object corresponding to `DigestResponse` from the REST API on success, or `nil` and an `Error` if the digest could not be computed. The returned digest is a `Blob`.

For example,

```bash
local sha256_hash = assert(digest { data = Blob.from_bytes('Hello world'), alg = 'SHA256' }).digest
return sha256_hash:hex()
```

### 2.19 The `BigNum` Class

A `BigNum` represents an arbitrary-precision integer.

**Constructors**

- `BigNum.from_bytes_be(string)`

Constructs a `BigNum` from a Lua string with big endian binary data.

For example, `local bignum = BigNum.from_bytes_be('\x01\x00\x01')`.
- `BigNum.from_bytes_be(blob)`

Constructs a `BigNum` from a Blob with big endian binary data.

E.g.: `local bignum = BigNum.from_bytes_be(Blob.from_bytes('\x01\x00\x01'))`.

**Methods**

- `bignum:to_bytes_be()`

Returns a Blob with unsigned binary data, big endian..

For example, `local bytes = bignum:to_bytes_be()`.
- `bignum:to_bytes_be_zero_pad(n)`

Returns a Blob with unsigned binary data, big endian, zero padding to n bytes.

For example, `local bytes = bignum:to_bytes_be_zero_pad(16)`.
- `bignum:copy()`

Returns a copy of bignum.

For example, ``local copy = bignum:copy()`.
- `bignum == bignum`

Compare two bignums for equality

For example, `local same = bignum1 == bignum2`
- `bignum &lt; bignum`

Test if one bignum is less than another

For example, `local less = bignum1 &lt; bignum2`
- `bignum &lt;= bignum`

Test if one bignum is less than or equal to another

For example, `local lte = bignum1 &lt;= bignum2`
- `bignum &gt; bignum`

Test if one bignum is greater than another

For example, `local greater = bignum1 &gt; bignum2`
- `bignum &gt;= bignum`

Test if one bignum is greater than or equal to another

For example, `local gte = bignum1 &gt;= bignum2`
- `bignum:add(bignum2)`

Performs a signed addition of bignum2 to bignum and updates bignum to result.

For example, `bignum:add(bignum2)`.
- `bignum + bignum2`

Performs a signed addition of bignum2 to bignum and returns result.

For example, `local result = bignum + bignum2`.
- `bignum:sub(bignum2)`

Performs a signed subtraction of bignum2 from bignum and updates bignum to result.

For example, `bignum:sub(bignum2)`.
- `bignum - bignum2`

Performs a signed subtraction of bignum2 from bignum and returns result.

For example, `local result = bignum - bignum2`.
- `bignum:mul(bignum2)`

Performs a multiplication of bignum by bignum2 and updates bignum to result.

For example, `bignum:mul(bignum2)`.
- `bignum * bignum2`

Performs a multiplication of bignum by bignum2 and returns result.

For example, `local result = bignum * bignum2`.
- `bignum:div()`

Performs a division of bignum by bignum2 and updates bignum to result.

For example, `bignum:div()`.
- `bignum / bignum2`

Performs a division of bignum by bignum2 and returns result.

For example, `local result = bignum / bignum2`.
- `bignum:mod()`

Performs a modular reduction of bignum for the base bignum2 and updates bignum to result.

For example, `bignum:mod()`.
- `bignum % bignum2`

Performs a modular reduction of bignum for the base bignum2 and returns result.

For example, `local result = bignum % bignum2`.

### 2.20 The `Time` Class

A `Time` represents current system time.

**Constructors**

- `Time.now_insecure()`

Constructs a `Time`.

For example, `local time = Time.now_insecure()`.
- `Time.from_iso8601(date_time)`

Constructs a `Time` representing the provided date/time which must be in ISO-8601 format. Returns an error if the provided input does not have the correct format or is outside the range supported by `Time`. Note that `Time` does not support date/times before Jan 1, 1970 00:00:00 UTC or after Apr 11, 2262 23:47:16 UTC.

For example, `local time = assert(Time.from_iso8601('20060102T150405Z'))`.

**Methods**

- `time:unix_epoch_seconds()`

Returns a Lua number representing `time` as seconds since Jan 1, 1970 00:00:00 UTC.

For example, `local secs = time:unix_epoch_seconds()`.
- `time:unix_epoch_nanoseconds()`

Returns a Lua number representing `time` as nanoseconds since Jan 1, 1970 00:00:00 UTC.

For example, `local nanosecs = time:unix_epoch_nanoseconds()`.
- `time:to_iso8601()`

Returns a Lua string representing `time` in ISO-8601 format.

For example,

```bash
local now = Time.now_insecure()
 return now:to_iso8601() -- for example: "20210914T193810Z"
```
- `time:add_seconds(seconds)`

Returns a new `Time` instance representing `seconds` after `time`. Note that `time` is not modified.

For example,

```bash
local t0 = assert(Time.from_iso8601('20060102T150405Z'))
 local t1 = t0:add_seconds(30)
 assert(t1:to_iso8601() == '20060102T150435Z')
```

### 2.21 The `OID` Class

An OID represents an ASN.1 object identifier.

**Constructors**

- `Oid.from_str(str)`

Load an OID from a string which can be either the usual dotted decimal notation or a name (for some well known and commonly used OIDs).

For example, `local cn = Oid.from_str('CN')` E.g.: `local email = Oid.from_str('1.2.840.113549.1.9.1')`

**Methods**

- `Oid:to_str()`

If the OID has a known value, return the string associated with it. Otherwise returns the dotted decimal value.

For example, `assert(email:to_str() == 'emailAddress')`

### 2.22 The `DerEncoder` Class

A `DerEncoder` allows creating DER encodings of arbitrary objects. This is useful when creating X.509 certificate extensions, or when the plugin wants to return an ASN.1 structure such as a CAdES signature.

**Constructors**

- `DerEncoder:new()`

Return a new `DerEncoder` object

**Methods**

- `DerEncoder:value()`

This returns the binary blob of the DER encoding so far. It is possible to call value more than once, and it is also possible to add more data to a `DerEncoder` after calling `value`. The sequences (calls to `start_seq`/`end_seq`) must be matched.
- `DerEncoder:start_seq()`

Being a new DER SEQUENCE structure. This can be nested.
- `DerEncoder:end_seq()`

End the currently active SEQUENCE
- `DerEncoder:implicit_context(tag)`

Tag the following datum with an implicit context-specific tag. For example, `DerEncoder.new():implicit_context(1):start_seq():implicit_context(2):add_int(2):end_seq()` will tag the SEQUENCE with a context specific tag of 1 and the INTEGER with a context specific tag of 2. The tag should be given as a non-negative integer.
- `DerEncoder:explicit_context(tag)`

Similar to `implicit_context` expect using explicit DER tagging.
- `DerEncoder:add_bool(b)`

Add a boolean value to the current DER encoding.
- `DerEncoder:add_int(i)`

Add an integer value to the current DER encoding. This can be either a Lua integer or a `BigNum` object.
- `DerEncoder:add_oid(oid)`

Add an `Oid` to the encoding.
- `DerEncoder:add_enum(en)`

Add an enum to the encoding; the enum should be specified as a Lua integer.
- `DerEncoder:add_string(str, encoding)`

Add a string to the encoding. The `encoding` parameter specifies what ASN.1 string type to use; 'utf8', 'printable', 'numeric' or 'ia5'. If the encoding paramter is not given or is nil, default to 'utf8'.

Be aware that when using 'printable' or 'numeric', the encoder does not check that the string satisfies the content requirements; it is possible to insert arbitrary data with 'numeric' or 'printable' types, which is an invalid DER encoding and is likely to be rejected by at least some parsers.
- `DerEncoder:add_octets(input)`

Add an OCTET STRING. The argument can be either a `Blob` or a Lua string. If the argument is a string it is assumed to be a hexadecimal input.
- `DerEncoder:add_raw(input)`

Insert arbitrary values into the DER encoding stream. The argument is directly inserted into the stream as-is with no inspection. This allows encoding values that are not expressible using the current API.

### 2.23 The `X509Name` Class

An `X509Name` represents an X.509 distinguished name, as used in certificates and signing requests.

**Constructors**

- `X509Name.from_der(der)`

Load a DN from the binary DER encoding.

For example, `local name = X509Name.from_der(der)`
- `X509Name.new()`

Create a new (empty) DN.

For example, `local name = X509Name.new()`

**Methods**

- `X509Name.to_str()`

Return a readable string encoding for the DN.
- `X509Name.oids()`

Return an array of OIDs specifying the values it contains
- `X509Name.get(oid)`

If the specified OID is set in the DN, return the value. Otherwise returns nil.
- `X509Name.set(oid, value, string_type)`

Set the OID to the provided value. The `string_type` field specifies how the string is encoded; for best interop use "utf8". Other accepted encodings are "printable", "numeric", and "ia5".

For example, `name.set(oids.from_str('CN'), 'localhost', 'utf8')`

### 2.24 The `Pkcs10Csr` Class

A `Pkcs10Csr` represents a certificate signing request as specified in PKCS 10.

**Constructors**

- `Pkcs10Csr.new(key_id, subject)`
- `Pkcs10Csr.from_der(der)`

Load a PKCS10 CSR from the binary DER value

**Methods**

- `csr:verify_signature()`

Verify the self-signature on a CSR, returning true on success. This simply verifies that whoever generated the CSR does have access to the private key corresponding to the public key in the CSR. This does not authenticate the CSR in any way. However, this prevents certain forgery attacks that are otherwise possible.
- `csr:subject_dn()`

Return an `X509Name` of the subject set in the CSR.
- `csr:sign(ca_cert, ca_key_name, cert_lifetime, digest, serial_bits, copy_extensions)`

Sign a CSR, creating a new certificate object. The `ca_cert` should be a CA certificate Sobject. The `ca_key_name` should be the name of a key stored in DSM which is associated with the issuing CA certificate. The certificate lifetime is given in seconds from the current time.

The `digest` parameter specifies what hash function to use when signing the certificate. If not specified (or `nil`), then SHA-256 is used by default.

The `copy_extensions` parameter's default value is false if not provided. This parameter determines if the extensions from CSR should be copied into a signed certificate or not.

### 2.25 The `TbsCertificate` Class

A `TbsCertificate` represents the body of a certificate without an associated signature. It allows creating a custom certificate without first creating a CSR.

**Constructors**

- `TbsCertificate.new(key_id, subject, skid, lifetime, serial_bits_size )`

The parameters have the following descriptions:
  - `key_id`: A security object descriptor (for example, a key name) for an asymmetric key.
  - `subject`: A string representing the X.509 distinguished name for the certificate (that is, information about the certificate owner).
  - `skid`: A string representing the “subject key identifier” (SKID) which will be included in the extensions of the resulting certificate. *For details on generating this value, refer to* [*https://www.rfc-editor.org/rfc/rfc3280#section-4.2.1.2.*](https://www.rfc-editor.org/rfc/rfc3280#section-4.2.1.2.)
  - `lifetime`: An integer representing the validity period of the certificate.
  - `serialize_size`: the size of the certificate’s serial number in bits. The parameter must be an integer. Valid values range from 8 to 160. If not provided, the default value is 160.

**Methods**

- `TbsCertificate:add_extension(oid, critical, value)`

Add an extension to the TbsCertificate.

Each certificate extension is identified by an OID and is marked either critical or non-critical. During validation, any implementation that encounters a critical extension it cannot identify is required to reject the certificate. So ordinarily critical extensions are only used when the certificate cannot be safely validated without understanding the extension. If the extension is just for informative purposes it is usually non-critical.

The value must be the complete DER encoding of the extension. This can be creating using DerEncoder, for example to insert a critical `basicConstraints` extension,

```bash
local bc = DerEncoder.new():start_seq():add_bool(false):end_seq():value()
 tbs:add_extension(Oid.from_str('basicConstraints'), true, bc)
```
- `TbsCertificate:to_der()`

Encode the TbsCertificate as a DER value
- `TbsCertificate:sign(ca_cert, ca_key_name, serial_bits, digest)`

Sign the TbsCertificate and return a new certificate object. The issuing certificate should be an Sobject and the `ca_key_name gives` the name of the signing key which must be stored in Fortanix DSM.

If `serial_bits` is set to a positive integer, then a new serial number is created of the specified bit size. If it is set to `true`, then a new serial of some default size (currently 160 bits) is used. If it is set to `0` or `false`, then the serial number set in the original TbsCertificate structure is used in the final signed certificate.

The `digest` parameter specifies what hash function to use when signing the certificate. If not specified (or `nil`), then SHA-256 is used by default.
- `TbsCertificate:self_sign(signing_key, digest)`

Self-sign a TbsCertificate, creating a new self-signed certificate, that is, with the issuer DN set the same as the subject DN that was provided when the `new` constructor was invoked.

The `signing_key` must be a reference to the private key which will be used to generate the signature.

The `digest` parameter specifies what hash function to use when signing the certificate. If not specified (or `nil`), then SHA-256 is used by default.

### 2.26 The `Tr31Envelope` Class

This represents a TR-31 cryptogram for key wrapping (refer to ANSI X9.143-2022). Wrapping a security object is achieved with the `Tr31Envelope.new` constructor followed by the `seal` function. Conversely, to unwrap an existing cryptogram, the function `tr31_open` is used.

**Constructors**

- `Tr31Envelope.new`

This prepares a TR-31 header to subsequently wrap an existing security object. Refer to ANSI X9.143-2022 for the meaning of these fields.

```lua
local envelope = Tr31Envelope.new(version, key_usage, algorithm, mode_of_use, key_version_number, exportability, key_context)
```

**Methods**

- `Tr31Envelope.seal`

Given a wrapping key and a target key (both residing in DSM), `seal` is used to perform the wrapping. It outputs the resulting TR-31 cryptogram, or an error.

```lua
local envelope = Tr31Envelope.new(version, key_usage, algorithm, mode_of_use, key_version_number, exportability, key_context)
local cryptogram, err = envelope:seal(wrapping_sobject, target_sobject)
```

### 2.27 The `tr31_open` Function

Given a TR-31 cryptogram and the corresponding unwrapping key (residing in DSM), this function unwraps the cryptogram, obtaining the unwrapped key bytes.

```lua
local key_bytes, err = tr31_open(unwrapping_sobject, cryptogram)
```

### 2.28 The `EcGroup` Class

An `EcGroup` represents some elliptic curve group supported in Fortanix DSM.

**Constructors**

- `EcGroup.SecP256R1`, `EcGroup.SecP256K1`, ...

EcGroup supports construction using a enum-style syntax. Possible values are "`Bp256R1`", "`Bp384R1`", "`Bp512R1`", "`Curve25519`", "`SecP192K1`", "`SecP192R1`", "`SecP224K1`", "`SecP224R1`", "`SecP256K1`", "`SecP256R1`", "`SecP384R1`", and "`SecP521R1`".
- `EcGroup.from_name`

This function constructs an EC group from a string (values matching the above named enums). This is useful when the desired group is provided as a parameter by the invoker of a plugin.

**Methods**

- `EcGroup:name()`

This function returns the name of the group.
- `EcGroup:generator()`

This function returns an `EcPoint` which is the generator of this group.
- `EcGroup:point_from_components(x, y)`

This function returns a new `EcPoint` taking `BigNum` components `x` and `y`.
- `EcGroup:point_from_binary(v)`

This function decodes an encoded EC point using the standard `OS2ECP` convention. Both compressed and uncompressed points are supported.

### 2.29 The `EcPoint` Class

An `EcPoint` represents a particular point on an elliptic curve. It is constructed using the functions on EcGroup.

**Methods**

- `EcPoint:group()`

This function returns the `EcGroup` that this point belongs to.
- `EcPoint:add(other)`

This function performs point addition, adding two points and returning the resulting point.
- `EcPoint:mul(scalar)`

This function performs point multiplication, multiplying a point by a scalar `BigNum` and returning the resulting point.
- `EcPoint:x()`

This function returns the affine x coordinate of the point as a `BigNum`.
- `EcPoint:y()`

This function returns the affine y coordinate of the point as a `BigNum`.
- `EcPoint:to_binary(compress)`

This function returns the binary encoding of a point. If `compress` is `false` then uncompressed encoding is used. If `compress` is `nil` or `true` then point compression is used.

### 2.30 The Function `require_approval_for`

You can call this function in your function `check` to require the caller to submit an approval request to invoke the plugin. For example,

```bash
function check(input)
  local key = assert(Sobject { name = 'my key' })
  require_approval_for(key)
end
```

In the above example, if `my key` is subject to an approval policy, then the caller needs to submit an approval request to invoke the plugin and the plugin is invoked once enough reviewers approve that request.

### 2.31 The `request` Function

The `request` function allows for HTTPS requests to be made to external servers. It consumes a Lua table with the following fields:

- `method`: a string indicating the HTTP method. For example, `"POST"`.
- `url`: the URL of the resource or server.
- `headers`: a table of any HTTP headers required for the request. If no headers are needed, use `headers = {}` )
- `body`: this payload for the request, which can be either:
  - a single string value. For example, `some_value=&lt;base64 input&gt;`
  - a JSON encoded block, useful for payloads with multiple fields. For example, `body = json.encode({ field1 = input.field1, field2 = some_other_data})`.

For example,

```bash
function run(input)
local url = 'https://apac.smartkey.io/sys/v1/version'
local headers = {}
headers['Content-Type'] = 'application/json'
local response, err = request { method = 'GET', url = url, headers = headers }
  return response
end
```

Fortanix Data Security Manager (DSM) is the world’s first cloud service secured with Intel® SGX. With Fortanix DSM, you can securely generate, store, and use cryptographic keys and certificates, as well as other secrets such as passwords, API keys, tokens, or any blob of data. Your business-critical applications and containers can integrate with Fortanix DSM using legacy cryptographic interfaces (PKCS#11, CNG, and JCE) or using the native Fortanix DSM RESTful interface.

## Related

- [Fortanix DSM with Microsoft SQL Server TDE - Standalone Server](/fortanix-dsm-with-microsoft-sql-server-tde-integration-standalone-server.md)
- [Plugin Library](/dsm-plugin-library.md)
- [Plugins - Getting Started](/fortanix-dsm-plugins-getting-started.md)
- [Fortanix DSM with Microsoft SQL Server TDE - Always On Availability Group (AG)](/fortanix-dsm-with-microsoft-sql-server-tde-integration-always-on-group-aog.md)
- [Group Key Metadata Policy](/fortanix-dsm-key-metadata-policy.md)
