Skip to content

[WIP] [knx] Add support for using hardware TPM modules#15326

Draft
holgerfriedrich wants to merge 2 commits into
openhab:mainfrom
holgerfriedrich:pr-knx-tpm
Draft

[WIP] [knx] Add support for using hardware TPM modules#15326
holgerfriedrich wants to merge 2 commits into
openhab:mainfrom
holgerfriedrich:pr-knx-tpm

Conversation

@holgerfriedrich

Copy link
Copy Markdown
Member

This as an attempt to store passwords for KNX secure protected by a TPM module.
The password is stored encrypted and can only be decrypted with this specific TPM.
The proof of concept was done on a Rapsberry PI with a LetsTrust TPM module on top.

  • Add TpmInterface, a class built on top of Tss.Java. This lib is to be included in a special way due to inconsistencies in package creation which makes it incompatible to OSGI.
  • Add console commands knx tpm-info and knx tpm-encrypt <password>. Output to be used instead of the password. Use space in front on the knx to avoid you password to be stored to console history.

Disclaimer: Use at you own risk.

Disclaimer: Storing encrypted passwords does not bring perfect security for the password - anyone who can access your machine can use the TPM as well to decode, RPI is not a secure system, Java is not secure at all. But it is nice that you do not need to worry about disclosing you password in backups or screenshots :-)

@holgerfriedrich holgerfriedrich added the work in progress A PR that is not yet ready to be merged label Jul 29, 2023
@lolodomo lolodomo added the enhancement An enhancement or new feature for an existing add-on label Jul 30, 2023
@holgerfriedrich holgerfriedrich force-pushed the pr-knx-tpm branch 3 times, most recently from d5b0877 to 1d8fa50 Compare July 31, 2023 22:07
@holgerfriedrich holgerfriedrich force-pushed the pr-knx-tpm branch 4 times, most recently from eafb63d to a66fcb5 Compare December 22, 2023 23:16
@holgerfriedrich holgerfriedrich force-pushed the pr-knx-tpm branch 2 times, most recently from 5285dbb to 8068b88 Compare February 20, 2024 18:46
@holgerfriedrich holgerfriedrich force-pushed the pr-knx-tpm branch 2 times, most recently from f25ee6f to 6a50352 Compare April 1, 2024 19:12
@holgerfriedrich holgerfriedrich force-pushed the pr-knx-tpm branch 2 times, most recently from 450711e to f761f32 Compare May 24, 2024 21:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for using hardware TPM (Trusted Platform Module) modules to securely store KNX passwords. The implementation provides TPM-based encryption and decryption capabilities, allowing passwords to be stored in an encrypted form that can only be decrypted on the specific machine with the TPM module.

  • Adds a new TpmInterface class that provides TPM operations including password encryption/decryption and hardware information retrieval
  • Introduces console commands knx tpm-info and knx tpm-encrypt for TPM interaction
  • Updates configuration classes to automatically decrypt TPM-protected passwords when they contain the special prefix

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
TpmInterface.java Core TPM interface implementation with encryption/decryption capabilities
TpmTest.java Test suite for TPM functionality with OS-specific test exclusions
KNXCommandExtension.java Adds new console commands for TPM information and password encryption
BridgeConfiguration.java Adds TPM decryption support for keyring passwords
IPBridgeConfiguration.java Adds TPM decryption for tunnel authentication passwords
pom.xml Integrates TSS.Java library with special OSGI handling
NOTICE Adds license information for TSS.Java dependency
Various test files Code style improvements and refactoring

Comment on lines +70 to +72
private static final String USER_PWD = Objects.requireNonNullElse(InstanceUUID.get(), "habOpen");
private static final TPMS_SENSITIVE_CREATE USER_AUTH = new TPMS_SENSITIVE_CREATE(new byte[0], USER_PWD.getBytes());

Copilot AI Aug 3, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a hardcoded fallback password 'habOpen' creates a security vulnerability. If InstanceUUID.get() returns null, all instances would use the same weak password. Consider using a cryptographically secure random value or throwing an exception instead.

Suggested change
private static final String USER_PWD = Objects.requireNonNullElse(InstanceUUID.get(), "habOpen");
private static final TPMS_SENSITIVE_CREATE USER_AUTH = new TPMS_SENSITIVE_CREATE(new byte[0], USER_PWD.getBytes());
private static final String USER_PWD = getUserPassword();
private static final TPMS_SENSITIVE_CREATE USER_AUTH = new TPMS_SENSITIVE_CREATE(new byte[0], USER_PWD.getBytes());
/**
* Returns the user password for TPM operations. Throws SecurityException if InstanceUUID is not available.
*/
private static String getUserPassword() {
String uuid = InstanceUUID.get();
if (uuid == null) {
throw new SecurityException("InstanceUUID is not available; cannot generate secure password for TPM.");
}
return uuid;
}

Copilot uses AI. Check for mistakes.
Comment on lines +393 to +397
for (int i = 3; i > 0; i--) {
sb.append((char) ((ret >> (i * 8)) & 0xff));
}
ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.VENDOR_STRING_4);
for (int i = 3; i > 0; i--) {

Copilot AI Aug 3, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loop condition should be i >= 0 to include all 4 bytes (indices 3, 2, 1, 0). Currently it excludes the byte at index 0, potentially truncating the vendor string.

Suggested change
for (int i = 3; i > 0; i--) {
sb.append((char) ((ret >> (i * 8)) & 0xff));
}
ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.VENDOR_STRING_4);
for (int i = 3; i > 0; i--) {
for (int i = 3; i >= 0; i--) {
sb.append((char) ((ret >> (i * 8)) & 0xff));
}
ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.VENDOR_STRING_4);
for (int i = 3; i >= 0; i--) {

Copilot uses AI. Check for mistakes.
Comment on lines +393 to +397
for (int i = 3; i > 0; i--) {
sb.append((char) ((ret >> (i * 8)) & 0xff));
}
ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.VENDOR_STRING_4);
for (int i = 3; i > 0; i--) {

Copilot AI Aug 3, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loop condition should be i >= 0 to include all 4 bytes (indices 3, 2, 1, 0). Currently it excludes the byte at index 0, potentially truncating the vendor string.

Suggested change
for (int i = 3; i > 0; i--) {
sb.append((char) ((ret >> (i * 8)) & 0xff));
}
ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.VENDOR_STRING_4);
for (int i = 3; i > 0; i--) {
for (int i = 3; i >= 0; i--) {
sb.append((char) ((ret >> (i * 8)) & 0xff));
}
ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.VENDOR_STRING_4);
for (int i = 3; i >= 0; i--) {

Copilot uses AI. Check for mistakes.
Comment on lines +160 to +185
/*
* TpmInterface.SecuredPassword passKey = new TpmInterface.SecuredPassword("", "", "");
* try {
* TpmInterface tpmIf = new TpmInterface();
* String tpmRev = tpmIf.getTpmVersion();
* String tpmModel = "unknown";
* try {
* tpmModel = tpmIf.getTpmModel();
* } catch (KNXException ignored) {
* }
* logger.info("TPM rev. {} detected, based on {}", tpmRev, tpmModel);
*
* passKey = tpmIf.encryptSecret("habOpen");
* logger.warn("{}", passKey);
* } catch (KNXException e) {
* logger.warn("TPM exception", e);
* }
* try {
* TpmInterface tpmIf = new TpmInterface();
* String pass = tpmIf.decryptSecret(passKey);
* logger.warn("TPM decoded: {}", pass);
* } catch (KNXException e) {
* logger.warn("TPM exception", e);
* }
*/

Copilot AI Aug 3, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This large block of commented-out code (lines 160-186) should be removed as it adds clutter and is not being used. If this is experimental code, consider moving it to a separate branch or documentation.

Suggested change
/*
* TpmInterface.SecuredPassword passKey = new TpmInterface.SecuredPassword("", "", "");
* try {
* TpmInterface tpmIf = new TpmInterface();
* String tpmRev = tpmIf.getTpmVersion();
* String tpmModel = "unknown";
* try {
* tpmModel = tpmIf.getTpmModel();
* } catch (KNXException ignored) {
* }
* logger.info("TPM rev. {} detected, based on {}", tpmRev, tpmModel);
*
* passKey = tpmIf.encryptSecret("habOpen");
* logger.warn("{}", passKey);
* } catch (KNXException e) {
* logger.warn("TPM exception", e);
* }
* try {
* TpmInterface tpmIf = new TpmInterface();
* String pass = tpmIf.decryptSecret(passKey);
* logger.warn("TPM decoded: {}", pass);
* } catch (KNXException e) {
* logger.warn("TPM exception", e);
* }
*/

Copilot uses AI. Check for mistakes.
console.println("TPM TCG Spec.: rev. " + TpmInterface.TPM.getTpmTcgRevision() + " level "
+ TpmInterface.TPM.getTpmTcgLevel());
} catch (SecurityException e) {
console.print("error: " + e.getMessage());

Copilot AI Aug 3, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message should use console.println() instead of console.print() to ensure proper line termination and consistent formatting with other console output.

Suggested change
console.print("error: " + e.getMessage());
console.println("error: " + e.getMessage());

Copilot uses AI. Check for mistakes.
}

} catch (SecurityException e) {
console.print("error: " + e.getMessage());

Copilot AI Aug 3, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message should use console.println() instead of console.print() to ensure proper line termination and consistent formatting with other console output.

Suggested change
console.print("error: " + e.getMessage());
console.println("error: " + e.getMessage());

Copilot uses AI. Check for mistakes.
@lsiepel

lsiepel commented Mar 30, 2026

Copy link
Copy Markdown
Contributor

@holgerfriedrich unfortunately i don;t have a knx system, so i can;t be much of help here. Anyway, i was wondering how you see the future for this PR. At the moment it is the olderst PR and that always gets a bit extra attention ;-)
Do we foresee this to be merged at all and/or what steps are there to be taken to get there?

@holgerfriedrich

Copy link
Copy Markdown
Member Author

Thanks for asking.
This PR gets a rebase from time to time, as I use it in production :-)

Though, there are a few reasons why I did not follow up in the last months:

  • base library - TSS.java - is basically unmaintained. It has been written as some kind of demo by a team at Microsoft, but does no longer get attention. My proof of concept for KNX uses RSA keys, taking up to a minute to compute. Elliptic curves would be much faster, but I could not get it running with a real TPM and TSS.java. No idea if it ever worked.
  • I did not find time to base on something more stable like tpm2-tss (C library) - maybe interfacing C libraries is finally easy when we move to Java 25??
  • basically this should move into core
  • I have some concerns that people kill their TPMs - this is not an issue on an RPi with a separately added TPM module, but might be on a more expensive board

So I don't know where this ends....

@holgerfriedrich holgerfriedrich force-pushed the pr-knx-tpm branch 2 times, most recently from ed677bd to 5eeba86 Compare May 13, 2026 11:46
Add TpmInterface, a class built on top of Tss.Java.
This lib is to be included in a special way due to inconsistencies
in package creation which makes it incompatible to OSGI.

Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement An enhancement or new feature for an existing add-on work in progress A PR that is not yet ready to be merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants