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
- pcall
- rawequal
- rawget
- rawlen
- rawset
- select
- setmetatable
- string
- table
- tonumber
- tostring
- type
- utf8
- xpcall
- _VERSION
- _G
The following will be supported soon:
- require
Support is not planned for the following:
- loadfile
- arg
- debug
- dofile
- package
- os
- io
The following Self-Defending Key Management Service (KMS) defined symbols are also in scope, detailed below:
- cbor
- json
- request
- require_approval_for
- Error
- Blob
- EntityId
- Sobject
- App
- User
- Plugin
- Group
- AuditLog
- principal
- this_plugin
- digest
- null
- Array
- BigNum
- Time
- EcGroup
- EcPoint
- Oid
- X509Name
- Pkcs10Csr
- TbsCertificate
- DerEncoder
Invoking plugins
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 injson.decode
, ornil
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?
), ornil
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 anError
: the plugin invocation errors as described in theError
section below.
The 'Blob' class
A Blob
represents arbitrary binary data.
Fortanix Self-Defending KMS 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 uses 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.
E.g.: local blob = Blob.from_bytes('\1\2\255')
.
Blob.from_base64(string)
Constructs a Blob
from base64-encoded data in a Lua string.
E.g.: local blob = Blob.from_base64('AQL/')
.
Blob.from_hex(string)
Constructs a Blob
from hex-encoded data in a Lua string.
E.g.: local blob = Blob.from_hex('0102FF')
.
Blob.from_base58(string)
Constructs a Blob
base58-encoded data in a Lua string.
E.g.: 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 an SHA-256d checksum.
E.g.: 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 Fortanix Self-Defending KMS'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, ...))
. See the documentation for string.pack
.
E.g.: assert(Blob.pack('>i8', 0xDEADBEEF):hex() == '00000000DEADBEEF')
Methods
blob:unpack(fmt)
This is equivalent to string.unpack(fmt, blob:bytes())
. See the documentation for string.unpack
.
E.g.: assert(Blob.from_hex('00000000DEADBEEF'):unpack('>i8') == 0xDEADBEEF)
blob:bytes()
Returns a Lua string with the raw binary data.
E.g.: assert(blob:bytes() == '\1\2\255')
blob:base64()
Returns a Lua string with the base64-encoded binary data.
E.g.: assert(blob:base64() == 'AQL/')
blob:hex()
Returns a Lua string with the hex-encoded binary data.
E.g.: 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.
E.g.: 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
..
maybe used to concatenate two blobs, e.g. Blob.from_bytes('\1') .. Blob.from_bytes('\2') == Blob.from_bytes('\1\2')
.
#blob
#
maybe used to get the number of bytes in the blob, e.g. assert(#Blob.from_bytes('\1\1\1') == 3)
.
blob & blob2, blob | blob2, blob ~ blob2
&
, |
, and ~
maybe 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.
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:
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)
.
- 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
- 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 somen
.
- A table is encoded as an array if the set of keys is
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,
null = setmetatable({}, { __tocbor = function(self) return cbor.encode(nil) end })
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.
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.
The function 'request'
request {method = method, url = request_url, headers = headers, body=request_body}
executes an arbitrary REST API request over HTTPS. The server must present a publicly-trusted certificate.
method
is the REST method name, POST, GET, PUT, etc.
request_url
is the https URL.
headers
is a map of custom headers to be sent, e.g: headers{['Content-Type'] = "application/json"}
.
request_body
is request data as a Lua string.
For example,
function run(input)
local method = "POST" -- GET, PUT etc
local request_url = "https://<rest-api-url>"
local headers = {['Content-Type'] = "application/json"}
local request_body = json.encode({key = "value"})
local response = request { method = method, url = request_url, headers = headers, body=request_body }
return json.decode(response.body:bytes()) -- returns json output
end
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.
The 'Array' class
An Array
is a table that always serializes to a 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]')
.
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 = <code>, message = "<message>" }
.
For example,
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
The function 'principal'
principal()
returns the EntityId
of the entity that invoked the plugin.
For example,
function run()
if principal():type() ~= 'user'
return nil, Error.new { status = 401, message = "Plugin may only be invoked by users" }
end
end
The function 'this_plugin'
this_plugin()
returns a plugin
object corresponding to the plugin being invoked.
For example,
function run()
return "the name of this plugin is " .. this_plugin().name
end
The function 'require_approval_for'
The require_approval_for(object)
ensures that quorum approval is met for the object. It returns nil
and an Error
if quorum approval is not met.
The object can be of type Sobject
, App
, Group
or Plugin
.
This requires the plugin to be invoked as an approval request using the REST API.
POST /sys/v1/approval_requests
{
"operation": "sys/v1/plugins/<plugin-UUID>",
"body": "plugin-input-body"
}
Example plugin code:
function check(input)
key = assert(Sobject { id = input })
require_approval_for(key)
end
function run(input)
return key:encrypt { plain = Blob.random(16), mode = 'ECB' }
end
Above will require quorum approval on the input key for the encryption operation.
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
maybe 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.
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 Blob
s (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 = ‘<uuid>’ } or Sobject { kid = ‘<uuid>’ }
This return the persisted security object with the given UUID, or nil
and an Error
if no such object exists.
For example,
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,
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,
local sobject = assert(Sobject.import { name = "my key", obj_type = "AES", value = Blob.random { bits = 128 } })
Methods
sobject:update { ... }
Update the properties of sobject
. This corresponds to PATCH /crypto/v1/keys/<uuid>
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,
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/<uuid>
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,
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,
local exported_value = assert(Sobject { name = 'my key' }):export().value
sobject:digest()
Retrieve the digest (hash) of the value of sobject
. This corresponds toPOST /crypto/v1/keys/digest
in the REST API.
This 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,
local sha256_hash = assert(Sobject { name = 'my key' }):digest().digest
return sha256_hash:hex()
sobject:descriptor()
Returns { kid = "<uuid>" }
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.
For example,
local sobject = assert(Sobject { name = "my aes key" })
local encrypt_response = assert(sobject:encrypt { plain = Blob.from_bytes("hello world"), mode = 'CBC' })
return encrypt_response.cipher:base64() .. ':' .. encrypt_response.iv:base64()
sobject:decrypt { … }
Decrypt data using sobject
. This corresponds to POST /crypto/v1/decrypt
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 encrypted.
In the retuned object, plain
is a Blob
.
For example,
function encrypt(blob)
local sobject = assert(Sobject { name = "my rsa key" })
return assert(sobject:decrypt { cipher = blob }).plain
end
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 an 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,
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 an 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,
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,
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,
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:persist{ ... }
If the sobject
is a transient key, this saves the key as a persisted sobject
. This corresponds to GET crypto/v1/keys/persist
in the REST API.
The arguments to this method are described in the REST API documentation for PersistTransientKeyRequest
.
This returns a sobject
which has a kid
set, or returns nil
and an Error
if the object could not be persisted.
For example,
local transient_sobject = assert(Sobject.create { obj_type = "AES", key_size = 128, transient = true })
local persisted_sobject = transient_sobject:persist{ name = "My Key"}
sobject:derive()
This derives a new key from an existing sobject
. This corresponds to GET crypto/v1/derive
in the REST API.
The arguments to this method are described in the REST API documentation for DeriveKeyRequestEx
.
This returns a sobject
for the derived key, or returns nil
and an Error
if the key derivation fails.
For example,
local new_sobject = assert(Sobject { name = "My Key" }):derive {
name = "New Key",
key_size = 128,
key_type = "AES",
mechanism = {
alg = "AES",
plain = Blob.from_bytes("my seed value"),
mode = "CBC"
}
}
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.
E.g.: 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.
E.g.: 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.
Sobject:ec_public_info()
If the object is an EC key, this returns information about the public part of the EC key.
Response object is {group = <EcGroup>, public_point = <Blob>}
Sobject:rsa_public_info()
If the object is an RSA key, this returns information about the public part of the RSA key.
Response object is {exponent = <Blob>, modulus = <Blob>, key_size = 1024}
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,
local key = assert(Sobject { name = "my key" })
local creator = key.creator
return "Created by a " .. creator:type() .. " named " .. creator:entity().name
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 = “<uuid>” }
This returns the app with the given UUID, or nil
and an Error
if no such app exists.
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 = “<uuid>” }
This returns the plugin with the given UUID, or nil
and an Error
if no such object exists.
The 'User' class
This represents a User. The properties of this object are described in the REST API docs.
Constructors
User { id = “<uuid>” }
This returns the user with the given UUID, or nil
and an Error
if no such user exists.
The 'Group' class
This represents a Group. The properties of this object are described in the REST API docs.
Constructors
Group { id = “<uuid>” }
This returns the group with the given UUID, or nil
and an Error
if no such group exists.
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,
local sha256_hash = assert(digest { data = Blob.from_bytes('Hello world'), alg = 'SHA256' }).digest
return sha256_hash:hex()
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.
E.g.: local bignum = BigNum.from_bytes_be('\x01\x00\x01')
.
BigNum.from_bytes_be(blob)
Constructs a BigNum
from a Blob with the 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 the big endian binary data.
E.g.: local bytes = bignum:to_bytes_be()
.
bignum:to_bytes_be_zero_pad(n)
Returns a Blob with big endian binary data, zero padding to n bytes.
E.g.: local bytes = bignum:to_bytes_be_zero_pad(16)
.
bignum:copy()
Returns a copy of bignum.
E.g.: local copy = bignum:copy()
.
bignum==bignum
Compare two bignums for equality
E.g.: local same = bignum1 == bignum2.
bignum < bignum
Test if one bignum is less than another
E.g.: local less = bignum1 < bignum2.
bignum <= bignum
Test if one bignum is less than or equal to another
E.g.: local lte = bignum1 <= bignum2.
bignum > bignum
Test if one bignum is greater than another
E.g.: local greater = bignum1 > bignum2.
bignum >= bignum
Test if one bignum is greater than or equal to another
E.g.: local gte = bignum1 >= bignum2.
bignum:add(bignum2)
Performs a signed addition of bignum2 to bignum and updates bignum to result.
E.g.: bignum:add(bignum2)
.
bignum + bignum2
Performs a signed addition of bignum2 to bignum and returns the result.
E.g.: local result = bignum + bignum2
.
bignum:sub(bignum2)
Performs a signed subtraction of bignum2 from bignum and updates bignum to result.
E.g.: bignum:sub(bignum2)
.
bignum - bignum2
Performs a signed subtraction of bignum2 from bignum and returns the result.
E.g.: local result = bignum - bignum2
.
bignum:mul(bignum2)
Performs a multiplication of bignum by bignum2 and updates bignum to result.
E.g.: bignum:mul(bignum2)
.
bignum * bignum2
Performs a multiplication of bignum by bignum2 and returns the result.
E.g.: local result = bignum * bignum2
.
bignum:div()
Performs a division of bignum by bignum2 and updates bignum to result.
E.g.: bignum:div()
.
bignum / bignum2
Performs a division of bignum by bignum2 and returns result.
E.g.: local result = bignum / bignum2
.
bignum:mod()
Performs a modular reduction of bignum for the base bignum2 and updates bignum to result.
E.g.: bignum:mod()
.
bignum % bignum2
Performs a modular reduction of bignum for the base bignum2 and returns result.
E.g.: local result = bignum % bignum2
.
The 'Time' class
A Time
represents current system time.
Constructors
Time.now_insecure()
Constructs a Time
.
E.g.: local time = Time.now_insecure()
.
Methods
time:unix_epoch_seconds()
Returns a Lua number tcorresponding to current system time as seconds since 00:00:00 Jan 1, 1970 UTC.
E.g.: local secs = time:unix_epoch_seconds()
.
time:unix_epoch_nanoseconds()
Returns a Lua number corresponding to current system time as nanoseconds since 00:00:00 Jan 1, 1970 UTC.
E.g.: local nanosecs = time:unix_epoch_nanoseconds()
.
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).
E.g.: 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.
E.g.: assert(email:to_str() == 'emailAddress')
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.
E.g.: local name = X509Name.from_der(der)
X509Name.new()
Create a new (empty) DN.
E.g.: 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".
E.g.: name.set(oids.from_str('CN'), 'localhost', 'utf8')
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:to_der()
Returns a Blob
with the DER encoding of the CSR.
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)
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 Fortanix Self-Defending KMS which is associated with the issuing CA certificate. The certificate lifetime is given in seconds from the current time. digest
the default value is SHA256 if not provided. serial_bits
the default value is 160 if not provided.
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)
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.
TbsCertificate:to_der()
Returns a Blob
with the DER encoding of the TbsCertificate.
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 Self-Defending KMS. digest
the default value is SHA256 if not provided. serial_bits
the default value is 160 if not provided.
TbsCertificate:self-sign(signing_key, digest)
Self-sign the TbsCertificate and return a new certificate object. The signing_key is the sobject
of the signing key which must be stored in Fortanix Self-Defending KMS. For the correct self-signed certificate, the public key in the TbsCertificate (key_id
in the constructor) must correspond to this signing key. digest
the default value is SHA256 if not provided.
The 'EcGroup' class
An EcGroup
represents some elliptic curve group supported in Fortanix Self-Defending KMS.
Constructors
EcGroup.SecP256R1
, EcGroup.SecP256K1
, ...
EcGroup supports construction via 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.
EcPoint
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.
The 'DerEncoder' class
A DerEncoder
provides ASN1 encoding functions which help in creating custom CSRs and Certificates.
Constructors
DerEncoder.new()
Methods
DerEncoder:start_seq()
This begins a DER SEQUENCE which is nested with a call to end_seq()
. The start_seq()
and end_seq()
must be correctly paired.
DerEncoder:end_seq()
This ends a DER SEQUENCE started by a start_seq()
earlier.
DerEncoder:add_bool(b)
DerEncoder:add_int(i)
Supports both int and bignum types for input.
DerEncoder:add_oid(oid)
Input is Oid
type.
DerEncoder:add_enum(i)
DerEncoder:add_string(s, encoding)
encoding
parameter specifies how the string is tagged. Possible values are utf8
, printable
, numeric
or ia5
. The encoding
is not verified for the input string by the system. Use utf8
unless required for format compatibility.
DerEncoder:add_octets(s)
Supports both Blob
type and a hex string for input.
DerEncoder:add_raw(s)
Supports both Blob
type and a hex string for input.
DerEncoder:implicit_context(tag)
This specifies that the next encoded value should be given an implicit context-specific tag.
DerEncoder:explicit_context(tag)
DerEncoder:value()
Returns a DER-encoded value as Blob
. If calls to start_seq()
and end_seq()
do not align, then function will return an error.
Example code
local oid = Oid.from_str('1.2.3.4')
local val = DerEncoder.new():start_seq()
:add_string("str", 'utf8')
:add_string("str", 'printable')
:add_string("dns", 'ia5')
:add_string('1234', 'numeric')
:add_bool(true)
:add_octets("01020304")
:add_oid(oid)
:end_seq():value()
return Blob.from_bytes(val):hex()