Skip to main content

ControlForge Cryptography & Security Guide

James M. Belcher Founder, JMB Technical Services LLC April 2026 | ControlForge v1.0.535


1. Overview

ControlForge provides ~55 built-in functions for cryptography, hashing, encoding, and authentication from Structured Text. Secure API calls, sign data, encrypt files, generate and verify JWTs, and validate checksums — all without external libraries.

CategoryFunctionsUse Case
Hashing10Data integrity, fingerprinting, checksums
HMAC5Message authentication, webhook signatures
AES Encryption6Symmetric encryption (CBC, GCM)
RSA5Asymmetric encryption, digital signatures
Base646Encoding binary data for transport
JWT12Token-based authentication
Utility8Key generation, constant-time compare, file encryption

2. Hashing

One-way hash functions — input to fixed-length digest. Cannot be reversed.

(* SHA-256 — most common, 64 hex chars *)
hash := SHA256('Hello World');
(* "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e" *)

(* SHA-512 — longer, stronger *)
hash := SHA512('Hello World');

(* MD5 — legacy, use SHA256 for new work *)
hash := MD5('Hello World');

(* SHA-1 — deprecated for security, still used in Git *)
hash := SHA1('Hello World');

(* SHA-384 — truncated SHA-512 *)
hash := SHA384('Hello World');

All return hex-encoded strings. For raw byte arrays:

bytes := SHA256_BYTES('Hello World'); (* Array of integers *)
bytes := MD5_BYTES('Hello World');

Checksums

(* CRC-32 — file integrity, Ethernet *)
crc := CRC32(data);

(* CRC-16 — generic *)
crc := CRC16(data);

(* CRC-16/Modbus — Modbus RTU frame validation *)
crc := CRC16_MODBUS(frame_data);

3. HMAC — Message Authentication

Hash-based Message Authentication Code — proves a message was created by someone with the secret key.

(* Sign a webhook payload *)
signature := HMAC_SHA256('my-secret-key', payload);

(* Verify incoming webhook *)
expected := HMAC_SHA256(webhook_secret, request_body);
IF HASH_EQUALS(expected, received_signature) THEN
(* Authentic — process webhook *)
END_IF;
FunctionReturnsDescription
HMAC_SHA256(key, message)STRING (hex)SHA-256 HMAC
HMAC_SHA512(key, message)STRING (hex)SHA-512 HMAC
HMAC_SHA1(key, message)STRING (hex)SHA-1 HMAC
HMAC_MD5(key, message)STRING (hex)MD5 HMAC
HMAC_SHA256_BASE64(key, message)STRING (base64)SHA-256 HMAC, base64-encoded

Always use HASH_EQUALS for comparison — never use = to compare hashes. HASH_EQUALS uses constant-time comparison to prevent timing attacks.


4. AES Encryption

Symmetric encryption — same key encrypts and decrypts. Three modes available:

ModeFunctionsUse Case
AES (default)AES_ENCRYPT / AES_DECRYPTGeneral purpose
AES-CBCAES_CBC_ENCRYPT / AES_CBC_DECRYPTBlock cipher, IV auto-generated
AES-GCMAES_GCM_ENCRYPT / AES_GCM_DECRYPTAuthenticated encryption (recommended)
(* Generate a random key *)
key := GENERATE_KEY(32); (* 256-bit key, base64-encoded *)

(* Encrypt *)
encrypted := AES_GCM_ENCRYPT('sensitive data', key);

(* Decrypt *)
plaintext := AES_GCM_DECRYPT(encrypted, key);
(* "sensitive data" *)

The IV/nonce is automatically generated and prepended to the ciphertext. Output is base64-encoded.

(* AES-CBC mode *)
encrypted := AES_CBC_ENCRYPT('secret message', key);
plaintext := AES_CBC_DECRYPT(encrypted, key);

(* Default AES mode *)
encrypted := AES_ENCRYPT('secret message', key);
plaintext := AES_DECRYPT(encrypted, key);

Use AES-GCM for new applications — it provides both encryption and authentication (detects tampering). CBC provides encryption only.


5. RSA — Asymmetric Encryption

Public key encrypts, private key decrypts. Also used for digital signatures.

Generate Key Pair

keys := RSA_GENERATE_KEYPAIR(2048); (* 2048-bit keys *)

private_key := JSON_GET_STRING(keys, 'private'); (* PEM-encoded *)
public_key := JSON_GET_STRING(keys, 'public'); (* PEM-encoded *)

(* Store keys *)
FILE_WRITE('/data/private.pem', private_key);
FILE_WRITE('/data/public.pem', public_key);

Encrypt / Decrypt

(* Encrypt with public key — anyone can encrypt *)
encrypted := RSA_ENCRYPT('secret message', public_key);

(* Decrypt with private key — only key holder can decrypt *)
plaintext := RSA_DECRYPT(encrypted, private_key);

Sign / Verify

(* Sign with private key — proves authenticity *)
signature := RSA_SIGN('important data', private_key);

(* Verify with public key — anyone can verify *)
valid := RSA_VERIFY('important data', signature, public_key);
IF valid THEN
(* Signature is authentic *)
END_IF;

6. Base64 Encoding

Encode binary data as ASCII text for safe transport in JSON, HTTP headers, and URLs.

(* Standard Base64 *)
encoded := BASE64_ENCODE('Hello World'); (* "SGVsbG8gV29ybGQ=" *)
decoded := BASE64_DECODE(encoded); (* "Hello World" *)

(* URL-safe Base64 (no padding, safe for URLs) *)
encoded := BASE64_URL_ENCODE('Hello World');
decoded := BASE64_URL_DECODE(encoded);

(* Byte array variants *)
encoded := BASE64_ENCODE_BYTES(byte_array);
bytes := BASE64_DECODE_BYTES(encoded);

B64_* aliases also available: B64_ENCODE, B64_DECODE, B64_URL_ENCODE, B64_URL_DECODE, B64_ENCODE_BYTES, B64_DECODE_BYTES.


7. JWT — JSON Web Tokens

Create, validate, and manage authentication tokens.

Create a Token

(* Build claims *)
claims := JWT_CREATE_CLAIMS(
'user-123', (* subject *)
'controlforge', (* issuer *)
'plant-api', (* audience *)
3600 (* expires in 1 hour *)
);

(* Add custom claims *)
claims := JWT_ADD_CLAIM(claims, 'role', 'operator');
claims := JWT_ADD_CLAIM(claims, 'plant', 'Plant-A');

(* Encode to JWT string *)
token := JWT_ENCODE(claims, 'my-secret-key');
(* "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ..." *)

Validate a Token

(* Quick verify — signature only *)
valid := JWT_VERIFY(token, 'my-secret-key');

(* Full validation — signature + expiry + issuer + audience *)
result := JWT_VALIDATE(token, 'my-secret-key', 'controlforge', 'plant-api');
IF JSON_GET_BOOL(result, 'valid') THEN
payload := JSON_GET(result, 'payload');
ELSE
error := JSON_GET_STRING(result, 'error');
END_IF;

Read Token Data

(* Get a specific claim *)
role := JWT_GET_CLAIM(token, 'role'); (* "operator" *)
sub := JWT_GET_CLAIM(token, 'sub'); (* "user-123" *)

(* Get all claims *)
all := JWT_GET_ALL_CLAIMS(token);

(* Get header info *)
alg := JWT_GET_HEADER(token, 'alg'); (* "HS256" *)

(* Check expiry *)
IF JWT_IS_EXPIRED(token) THEN
(* Token expired — refresh or reject *)
END_IF;

remaining := JWT_TIME_TO_EXPIRY(token); (* Seconds until expiry *)

Refresh a Token

(* Create new token with fresh expiry, same claims *)
new_token := JWT_REFRESH(token, 'my-secret-key', 3600);

Decode Without Verification

(* Decode (does NOT verify signature — for inspection only) *)
parts := JWT_DECODE(token);
(* Returns: {header: {...}, payload: {...}, signature: "..."} *)

8. Utility Functions

Key and IV Generation

(* Generate random encryption key — default 32 bytes (256-bit) *)
key := GENERATE_KEY(32); (* 32 bytes = AES-256 *)
key := GENERATE_KEY(16); (* 128-bit key *)

(* Generate random initialization vector — 16 bytes *)
iv := GENERATE_IV();

Aliases: RANDOM_KEY(), RANDOM_IV()

Constant-Time Comparison

(* ALWAYS use for comparing hashes/secrets — prevents timing attacks *)
match := HASH_EQUALS(computed_hash, expected_hash);
match := SECURE_COMPARE(a, b); (* Alias *)

(* Verify a hash against data *)
valid := HASH_VERIFY('sha256', data, expected_hash);

File Encryption

(* Encrypt a file in place *)
ok := ENCRYPT_FILE('/data/sensitive.csv', encryption_key);

(* Decrypt a file in place *)
ok := DECRYPT_FILE('/data/sensitive.csv', encryption_key);

9. Complete Example: Secure API Client

Authenticate with JWT and sign requests with HMAC:

PROGRAM POU_SecureAPI
VAR
state : INT := 0;
token : STRING;
claims : STRING;
api_key : STRING := 'my-api-secret';
payload : STRING;
signature : STRING;
hdrs : STRING;
resp : STRING;
END_VAR

CASE state OF
0: (* Create JWT for authentication *)
claims := JWT_CREATE_CLAIMS('goplc-plant1', 'controlforge', 'cloud-api', 3600);
claims := JWT_ADD_CLAIM(claims, 'plant_id', 'PLANT-001');
token := JWT_ENCODE(claims, api_key);
state := 10;

10: (* Build signed request *)
payload := JSON_STRINGIFY(JSON_OBJECT(
'temperature', 72.5,
'pressure', 45.3
));

(* HMAC signature for request body *)
signature := HMAC_SHA256(api_key, payload);

(* Build headers *)
hdrs := HTTP_SET_HEADER('', 'Authorization', CONCAT('Bearer ', token));
hdrs := HTTP_SET_HEADER(hdrs, 'X-Signature', signature);

resp := HTTP_REQUEST('POST', 'https://api.example.com/telemetry',
payload, hdrs, 10);

IF HTTP_OK(resp) THEN
state := 10; (* Loop *)
END_IF;

(* Refresh token before expiry *)
IF JWT_TIME_TO_EXPIRY(token) < 300 THEN
token := JWT_REFRESH(token, api_key, 3600);
END_IF;
END_CASE;
END_PROGRAM

10. Complete Example: Encrypted Data Logger

Encrypt sensitive process data at rest:

PROGRAM POU_EncryptedLog
VAR
initialized : BOOL := FALSE;
scan_count : DINT := 0;
key : STRING;
line : STRING;
encrypted_line : STRING;
END_VAR

IF NOT initialized THEN
(* Load or generate encryption key *)
IF FILE_EXISTS('/data/log.key') THEN
key := FILE_READ('/data/log.key');
ELSE
key := GENERATE_KEY(32);
FILE_WRITE('/data/log.key', key);
END_IF;
initialized := TRUE;
END_IF;

scan_count := scan_count + 1;

IF (scan_count MOD 100) = 0 THEN
(* Build log entry *)
line := CONCAT(
DT_TO_STRING(NOW()), ',',
REAL_TO_STRING(temperature), ',',
REAL_TO_STRING(pressure)
);

(* Encrypt and append *)
encrypted_line := AES_GCM_ENCRYPT(line, key);
FILE_APPEND('/data/encrypted_log.dat', CONCAT(encrypted_line, CHR(10)));
END_IF;
END_PROGRAM

Appendix A: Quick Reference

Hashing (10)

FunctionReturnsDescription
SHA256(data)STRING (hex)SHA-256 hash
SHA384(data)STRING (hex)SHA-384 hash
SHA512(data)STRING (hex)SHA-512 hash
SHA1(data)STRING (hex)SHA-1 hash
MD5(data)STRING (hex)MD5 hash
SHA256_BYTES(data)ARRAYSHA-256 as byte array
MD5_BYTES(data)ARRAYMD5 as byte array
CRC32(data)INTCRC-32 checksum
CRC16(data)INTCRC-16 checksum
CRC16_MODBUS(data)INTCRC-16/Modbus

HMAC (5)

FunctionReturnsDescription
HMAC_SHA256(key, msg)STRING (hex)HMAC-SHA256
HMAC_SHA512(key, msg)STRING (hex)HMAC-SHA512
HMAC_SHA1(key, msg)STRING (hex)HMAC-SHA1
HMAC_MD5(key, msg)STRING (hex)HMAC-MD5
HMAC_SHA256_BASE64(key, msg)STRING (b64)HMAC-SHA256, base64 output

AES (6)

FunctionReturnsDescription
AES_ENCRYPT(text, key)STRING (b64)AES encrypt (IV auto-generated)
AES_DECRYPT(cipher, key)STRINGAES decrypt
AES_CBC_ENCRYPT(text, key)STRING (b64)AES-CBC encrypt
AES_CBC_DECRYPT(cipher, key)STRINGAES-CBC decrypt
AES_GCM_ENCRYPT(text, key)STRING (b64)AES-GCM authenticated encrypt
AES_GCM_DECRYPT(cipher, key)STRINGAES-GCM authenticated decrypt

RSA (5)

FunctionReturnsDescription
RSA_GENERATE_KEYPAIR([bits])MAP{private, public} PEM keys
RSA_ENCRYPT(text, pubkey)STRING (b64)Encrypt with public key
RSA_DECRYPT(cipher, privkey)STRINGDecrypt with private key
RSA_SIGN(msg, privkey)STRING (b64)Sign with private key
RSA_VERIFY(msg, sig, pubkey)BOOLVerify signature

Base64 (6)

FunctionReturnsDescription
BASE64_ENCODE(str)STRINGStandard base64
BASE64_DECODE(str)STRINGDecode base64
BASE64_URL_ENCODE(str)STRINGURL-safe base64 (no padding)
BASE64_URL_DECODE(str)STRINGDecode URL-safe base64
BASE64_ENCODE_BYTES(arr)STRINGEncode byte array
BASE64_DECODE_BYTES(str)ARRAYDecode to byte array

JWT (12)

FunctionReturnsDescription
JWT_CREATE_CLAIMS([sub,iss,aud,exp])MAPBuild standard claims
JWT_ADD_CLAIM(claims, key, val)MAPAdd custom claim
JWT_ENCODE(claims, secret [,alg])STRINGCreate JWT string
JWT_DECODE(token)MAPDecode without verification
JWT_VERIFY(token, secret)BOOLVerify signature
JWT_VALIDATE(token, secret [,iss,aud])MAPFull validation
JWT_GET_CLAIM(token, name)ANYRead single claim
JWT_GET_ALL_CLAIMS(token)MAPRead all claims
JWT_GET_HEADER(token, field)ANYRead header field
JWT_IS_EXPIRED(token)BOOLCheck expiry
JWT_TIME_TO_EXPIRY(token)INTSeconds remaining
JWT_REFRESH(token, secret, exp)STRINGNew token, fresh expiry

Utility (8)

FunctionReturnsDescription
GENERATE_KEY([len])STRING (b64)Random key (default 32 bytes)
GENERATE_IV()STRING (b64)Random 16-byte IV
HASH_EQUALS(a, b)BOOLConstant-time compare
HASH_VERIFY(alg, data, hash)BOOLVerify hash matches data
SECURE_COMPARE(a, b)BOOLAlias for HASH_EQUALS
ENCRYPT_FILE(path, key)BOOLEncrypt file in place
DECRYPT_FILE(path, key)BOOLDecrypt file in place

ControlForge v1.0.535 | ~55 Crypto & Security Functions | SHA/AES/RSA/JWT/HMAC/Base64

© 2026 JMB Technical Services LLC. All rights reserved. Back to All Guides