TapHome

Encryption and hashes

TapHome script functions for encrypted and signed device protocols: AESENCRYPT and AESDECRYPT, HASH and HMAC with MD5 and SHA, RANDBYTES, and a Bosch KM200 example.

The encryption and hash functions build what a device with an encrypted or signed local interface expects: AES encryption, MD5 and SHA hashes, HMAC signatures and secure random bytes. They only build values, so they work in every script. All of them need Core 2026.1 on TapHome Next, build 40991 or newer (not yet in the official release). Base64, hexadecimal text and CRC checksums are in Byte encodings and checksums.

They take byte data: a collection<uint8>, a blob or a string. A string is used as ISO-8859-1 bytes. Unlike TOBYTES without an encoding, which turns other characters into ?, a character outside ISO-8859-1 is an error here — for UTF-8 pass TOBYTES(text, "utf-8"). The result is a collection<uint8>.

HASH, HMAC

HASH computes the hash of byte data, HMAC a keyed hash (a signature) of it.

1
2
HASH(data, algorithm)
HMAC(key, data, algorithm)
NameDescription
dataByte data to hash.
keyKey of HMAC, byte data.
algorithm"MD5", "SHA1", "SHA256" or "SHA512", not case sensitive; "SHA-1", "SHA-256" and "SHA-512" are accepted too.

Returns — collection<uint8> of 16, 20, 32 or 64 bytes.

Remarks — HMAC takes the key first, in the same order as Python’s hmac.new(key, msg, digest).

Example

TOHEX(HASH("abc", "MD5"));
# Result: "900150983cd24fb0d6963f7d28e17f72"
TOHEX(HMAC("Jefe", "what do ya want for nothing?", "SHA256"));
# Result: "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"

Errors — HASH Unknown algorithm. Valid values are ‘MD5’, ‘SHA1’, ‘SHA256’, ‘SHA512’; HASH - data contains characters outside ISO-8859-1, convert the text with TOBYTES(text, “utf-8”); HASH - data must be a uint8 collection, a blob or a string. The messages of HMAC start with its own name and name the argument.

Available — Core 2026.1 on TapHome Next, build 40991 or newer (not yet in the official release).

AESENCRYPT, AESDECRYPT

AESENCRYPT encrypts byte data with AES, AESDECRYPT decrypts it.

1
2
AESENCRYPT(data, key, mode [, iv])
AESDECRYPT(data, key, mode [, iv])
NameDescription
dataByte data. Data for AESDECRYPT must have a length that is a multiple of 16 bytes.
key16, 24 or 32 bytes: AES-128, AES-192 or AES-256.
mode"ECB" or "CBC", optionally followed by the padding, for example "CBC/PKCS7" or "ECB/ZEROS"; not case sensitive.
iv16 bytes, required by CBC. ECB takes none — leave the argument out or pass Null.
PaddingAESENCRYPTAESDECRYPT
/PKCS7 (default; /PKCS5, the Java name, means the same)Adds the padding.Removes it.
/ZEROSFills the last block with 0x00 bytes; data that already ends on a block boundary gets none, empty data stays empty.Removes all trailing 0x00 bytes, also those that belong to the data — use it for text such as JSON.
/NONENo padding; the data length must be a multiple of 16 bytes.Removes nothing.

Returns — collection<uint8>.

Remarks — Only /PKCS7 can notice a wrong key, and not always; with /ZEROS and /NONE a wrong key returns wrong bytes without an error. A wrong iv in CBC garbles only the first 16 bytes and is never reported.

Example

VAR key := BYTECOLLECTION("2b7e151628aed2a6abf7158809cf4f3c");
VAR iv := BYTECOLLECTION("000102030405060708090a0b0c0d0e0f");
VAR secret := AESENCRYPT("Hello World", key, "CBC", iv);
TOSTRING(AESDECRYPT(secret, key, "CBC", iv));
# Result: "Hello World"

Errors — AESDECRYPT - decryption failed, check the key, the iv and the padding (the /PKCS7 padding does not match: a wrong key, or data not encrypted with PKCS7); AESENCRYPT - key must be 16, 24 or 32 bytes long (AES-128, AES-192, AES-256), it is N; AESENCRYPT - CBC mode needs a 16-byte iv, it is N; AESENCRYPT - ECB mode does not use an iv, leave the argument out; AESENCRYPT Unknown mode. Valid values are ‘ECB’, ‘CBC’, optionally followed by ‘/PKCS7’ (or ‘/PKCS5’), ‘/ZEROS’ or ‘/NONE’; AESENCRYPT - with padding NONE the data length must be a multiple of 16 bytes, it is N; AESDECRYPT - encrypted data length must be a multiple of 16 bytes, it is N. Each function names itself in the message.

Available — Core 2026.1 on TapHome Next, build 40991 or newer (not yet in the official release).

RANDBYTES

RANDBYTES returns cryptographically secure random bytes — for a CBC iv, a nonce or a session key.

1
RANDBYTES(count)

Returns — collection<uint8> of count bytes, 1 to 2048; the same as Python’s secrets.token_bytes(n) or os.urandom(n).

Remarks

  • RAND and RANDINT are not secure and must not be used for a key, an iv or a nonce.
  • A collection variable assigned to another variable is the same collection, so take a COPY before you APPEND to it — otherwise the iv grows too.

Example — a fresh iv in front of the encrypted data, as many protocols send it

VAR key := FROMHEX("2b7e151628aed2a6abf7158809cf4f3c");
VAR iv := RANDBYTES(16);
VAR body := COPY(iv);
APPEND(body, AESENCRYPT("Hello World", key, "CBC", iv));
TOSTRING(AESDECRYPT(COPY(body, 16), key, "CBC", COPY(body, 0, 16)));
# Result: "Hello World"

Errors — RANDBYTES: count must be between 1 and 2048.

Available — Core 2026.1 on TapHome Next, build 40991 or newer (not yet in the official release).

Example — Bosch / Buderus KM200 gateway

The HTTP interface of a Bosch / Buderus KM200 gateway, also used by IVT Rego 2000, as community projects describe it (the manufacturer does not publish it). The body is Base64 of AES-256-ECB data padded with zeros, and the key is made of two MD5 hashes:

VAR salt := BYTECOLLECTION("00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff");  # 32-byte vendor salt
VAR k1 := TOBYTES(REPLACE("xxxx-xxxx-xxxx-xxxx", "-", ""));    # gateway password
APPEND(k1, salt);
VAR k2 := COPY(salt);
APPEND(k2, TOBYTES("my private password"));
VAR key := HASH(k1, "MD5");
APPEND(key, HASH(k2, "MD5"));                                  # 32 bytes = AES-256

VAR r := SENDHTTPREQUEST("/system/healthStatus", "GET", "", "User-Agent: TeleHeater/2.2.3");
# value = a module variable that keeps the result
IF r.IsSuccess
    value := PARSEJSON(TOSTRING(AESDECRYPT(FROMBASE64(r.Content), key, "ECB/ZEROS"), "utf-8"), "value");
ELSE
    ADDERROR(r.StatusCode + " (" + r.ReasonPhrase + ")");
END

See also — Packet parser functions, Byte encodings and checksums, Text and collections.