← Back to Guides

Cryptography & Security Guide

GoPLC Cryptography & Security Guide

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


1. Overview

GoPLC 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 *)
    'goplc',              (* 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', 'goplc', '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();
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', 'goplc', '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

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

© 2026 JMB Technical Services LLC. All rights reserved.