Setting Up Rule Server
Before we can set up meaningful Policies for the Vertex, we first need to set up a Rule Server.
Rule Server Signing Requests
The Rule Server will receive POST requests from the Vertex to the URL provided when defining the Rule in the Vertex.
Each request will be a JWT that is signed by the Vertex.
In order to verify the JWT, the Rule Server must have the Vertex public key, that can be retrieved in PEM format using:
curl -X GET 'https://<YOUR_VERTEX>/identity-pubkey' > vertex_public_key.pem
The public key can then be used to verify the JWT signature using the ES256
algorithm.
Generating a JWT Keypair
The Vertex supports both the ES256
(our recommendation) and RS256
(RSA 2048-bit) algorithms for Rule Server JWTs.
To generate a keypair for the Rule Server, you can use the following commands:
- ES256
- RS256
# Generating the private key
openssl ecparam -name prime256v1 -genkey -noout -out es256_private.pem
# Generating the public key in PEM format
openssl ec -in es256_private.pem -pubout -out es256_public.pem
# Generating the private key
openssl genrsa -out rule_server_private.pem 2048
# Generating the public key in PEM format
openssl rsa -in rule_server_private.pem -outform PEM -pubout -out rule_server_public.pem
Request Data Spec
The JWT will contain a JSON structure that always includes an action
field, each action
has a different data structure:
- sign_ecdsa
- sign_ed25519
- sign_bip340
{
action: 'sign_ecdsa',
request: {
hash_algo: 'keccak256' | 'sha256' | 'sha256d' | 'none', // The hash algorithm used to hash the message
msg: '1a2b3c4d5e6f', // The message to be signed in hex format
derivation_path: [ 44, 66, 0, 0, 0 ], // The derivation path of the key to use for signing
derived_pubkey: '04bd59...99ec' // The uncompressed derived public key of the key to use for signing
},
extra_data: '', // Any extra data (in hex format) that was sent with the original signing request to help the Rule Server validate the request
request_id: 'f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42' // A unique identifier for the request, must be included in the response.
}
{
action: 'sign_ed25519',
request: {
msg: '1a2b3c4d5e6f', // The message to be signed in hex format
derivation_path: [ 44, 66, 0, 0, 0 ], // The derivation path of the key to use for signing
derived_pubkey: '04bd59...99ec' // The derived public key of the key to use for signing
},
extra_data: '', // Any extra data (in hex format) that was sent with the original signing request to help the Rule Server validate the request
request_id: 'f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42' // A unique identifier for the request, must be included in the response.
}
{
action: 'sign_bip340',
request: {
msg: '1a2b3c4d5e6f', // The message to be signed in hex format
derivation_path: [ 44, 66, 0, 0, 0 ], // The derivation path of the key to use for signing
tweak: '6f5e4d3c2b1a', // The (optional) tweak to use for the BIP340 signature in hex format.
derived_pubkey: '04bd59...99ec' // The derived public key of the key to use for signing
},
extra_data: '', // Any extra data (in hex format) that was sent with the original signing request to help the Rule Server validate the request
request_id: 'f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42' // A unique identifier for the request, must be included in the response.
}
Response Data Spec
The Rule Server must respond with a JWT that is signed by the Rule Server's private key.
The structure of the response JWT must be one of the following:
- approved
- rejected
- retry
{
result: 'approved', // The result of the request, must be 'approved' to sign the message
request_id: 'f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42' // The request_id from the request
}
{
result: 'rejected', // The result of the request, 'rejected' will fail the signing operation
request_id: 'f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42' // The request_id from the request
reason: 'Invalid message' // The reason for rejecting the request, the Vertex will report this string to the User
}
{
result: 'retry', // The result of the request, 'retry' will cause the Vertex to retry the request (up to a maximum of 3 retries)
request_id: 'f032f5c0-e69a-4f86-8ef9-9ac79e8fbc42' // The request_id from the request
reason: 'Invalid message' // The reason for retrying the request, the Vertex will report this string to the User in case all retries fail.
}
Parsing the Request
The Rule Server must validate the JWT signature using the Vertex public key.
Then, the Rule Server must parse the request data and validate the message or transaction that is being signed.
A simple example is provided below:
const express = require('express');
const jwt = require('jsonwebtoken');
const fs = require('fs');
// Load the Rule Server private key and the Vertex public key
const privateKey = fs.readFileSync('rule_private.pem');
const vertexPubkey = fs.readFileSync('vertex_public_key.pem');
const app = express();
const port = 80;
app.use(function (req) {
req.decodedBody = '';
req.setEncoding('utf8');
req.on('data', function (chunk) {
req.decodedBody += chunk;
});
req.on('end', function () {
req.next();
});
});
app.post('/validate_transaction', (req, res) => {
let signRequest = '';
try {
signRequest = jwt.verify(req.decodedBody, vertexPubkey)
} catch (error) {
res.sendStatus(401); // Unauthorized
};
if (signRequest) {
const { request_id } = signRequest; // request_id must be returned in the response
console.log('Sign request data', signRequest); // For this example we only print the request
let result = 'approved'; // We will approve all requests in this example
const jwtRes = jwt.sign(
{
result,
request_id,
},
privateKey,
{ algorithm: 'RS256' } // For this example we use RS256
);
res.send(jwtRes);
}
});
app.listen(port, () => {
console.log(`Server listening at http://0.0.0.0:${port}`);
});
Next Steps
Once the Rule Server is set up, you can define the Rule in the Vertex and create a Policy that includes the Rule.
Full details on setting up Policies for the Vertex can be found here.