Skip to content

Commit 2d18611

Browse files
single instance
Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
1 parent f68ef3f commit 2d18611

4 files changed

Lines changed: 148 additions & 84 deletions

File tree

bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/config/BridgeConfiguration.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,8 @@ protected String decrypt(String secret) {
6060
if (secret.startsWith(KNXBindingConstants.ENCYRPTED_PASSWORD_SERIALIZATION_PREFIX)) {
6161
try {
6262
logger.info("trying to access TPM module");
63-
if (tpmIf == null) {
64-
tpmIf = new TpmInterface();
65-
logger.info("generating keys, this might take some time");
66-
}
67-
TpmInterface tmpTpmIf = tpmIf;
68-
if (tmpTpmIf != null) {
69-
secret = tmpTpmIf.deserializeAndDectryptSecret(
70-
secret.substring(KNXBindingConstants.ENCYRPTED_PASSWORD_SERIALIZATION_PREFIX.length()));
71-
} else {
72-
logger.error("Unable to decode stored password using TPM");
73-
}
63+
return TpmInterface.TPM.deserializeAndDectryptSecret(
64+
secret.substring(KNXBindingConstants.ENCYRPTED_PASSWORD_SERIALIZATION_PREFIX.length()));
7465
} catch (SecurityException e) {
7566
logger.error("Unable to decode stored password using TPM: {}", e.getMessage());
7667
// fall through

bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/console/KNXCommandExtension.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,26 +68,28 @@ public void execute(String[] args, Console console) {
6868
} else if (args.length == 1 && CMD_TPM_INFO.equalsIgnoreCase(args[0])) {
6969
try {
7070
console.println("trying to access TPM module");
71-
TpmInterface tpm = new TpmInterface();
72-
console.println("TPM version: " + tpm.getTpmVersion());
73-
console.println("TPM model: " + tpm.getTpmManufacturerShort() + " " + tpm.getTpmModel());
74-
console.println("TPM firmware: " + tpm.getTpmFirmwareVersion());
75-
console.println("TPM TCG Spec.: rev. " + tpm.getTpmTcgRevision() + " level " + tpm.getTpmTcgLevel());
71+
console.println("TPM version: " + TpmInterface.TPM.getTpmVersion());
72+
console.println("TPM model: " + TpmInterface.TPM.getTpmManufacturerShort() + " "
73+
+ TpmInterface.TPM.getTpmModel());
74+
console.println("TPM firmware: " + TpmInterface.TPM.getTpmFirmwareVersion());
75+
console.println("TPM TCG Spec.: rev. " + TpmInterface.TPM.getTpmTcgRevision() + " level "
76+
+ TpmInterface.TPM.getTpmTcgLevel());
7677
} catch (SecurityException e) {
7778
console.print("error: " + e.getMessage());
7879
}
7980
return;
8081
} else if (args.length == 2 && CMD_TPM_ENCRYPT.equalsIgnoreCase(args[0])) {
8182
try {
8283
console.println("trying to access TPM module");
83-
TpmInterface tpm = new TpmInterface();
84-
console.println("generating keys, this might take some time");
85-
String p = tpm.encryptAndSerializeSecret(args[1]);
84+
if (!TpmInterface.TPM.isReady()) {
85+
console.println("generating keys, this might take some time");
86+
}
87+
String p = TpmInterface.TPM.encryptAndSerializeSecret(args[1]);
8688
console.println("encrypted representation of password");
8789
console.println(KNXBindingConstants.ENCYRPTED_PASSWORD_SERIALIZATION_PREFIX + p);
8890

8991
// check if TPM can decrypt
90-
String decrypted = tpm.deserializeAndDectryptSecret(p);
92+
String decrypted = TpmInterface.TPM.deserializeAndDectryptSecret(p);
9193
if (args[1].equals(decrypted)) {
9294
console.println("Password successfully recovered from encrypted representation");
9395
} else {

bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/tpm/TpmInterface.java

Lines changed: 118 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@
5959
* @author Holger Friedrich - Initial contribution
6060
*/
6161
@NonNullByDefault
62-
public class TpmInterface {
62+
public enum TpmInterface {
63+
TPM;
64+
6365
private final Logger logger = LoggerFactory.getLogger(TpmInterface.class);
6466
private static final byte[] STANDARD_EK_POLICY = Helpers
6567
.fromHex("837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa");
@@ -70,7 +72,8 @@ public class TpmInterface {
7072

7173
private @Nullable CreatePrimaryResponse rsaEk;
7274
private @Nullable CreatePrimaryResponse rsaSrk;
73-
private Tpm tpm;
75+
private @Nullable Tpm tpm;
76+
private @Nullable StartAuthSessionResponse policySession;
7477

7578
public record SecuredPassword(String secret, String encIdentity, String integrityHMAC) implements Serializable {
7679
private static final long serialVersionUID = 238409238L;
@@ -81,28 +84,65 @@ public record SecuredPassword(String secret, String encIdentity, String integrit
8184
*
8285
* @throws SecurityException
8386
*/
84-
public TpmInterface() throws SecurityException {
85-
try {
86-
@Nullable
87-
Tpm tmpTpm = TpmFactory.platformTpm();
88-
if (tmpTpm == null) {
87+
private TpmInterface() {
88+
}
89+
90+
private void init() throws SecurityException {
91+
if (tpm == null) {
92+
initSynchronized();
93+
}
94+
}
95+
96+
private synchronized void initSynchronized() throws SecurityException {
97+
if (tpm == null) {
98+
try {
99+
tpm = TpmFactory.platformTpm();
100+
} catch (TpmException e) {
101+
throw new SecurityException("TPM cannot be accessed", e);
102+
}
103+
if (tpm == null) {
89104
throw new SecurityException("TPM cannot be accessed");
90-
} else {
91-
tpm = tmpTpm;
92105
}
93-
} catch (TpmException e) {
94-
throw new SecurityException("TPM cannot be accessed", e);
95106
}
96107
}
97108

109+
public boolean isAvailable() {
110+
if (tpm != null) {
111+
return true;
112+
}
113+
try {
114+
init();
115+
if (tpm != null) {
116+
return true;
117+
}
118+
} catch (SecurityException e) {
119+
logger.info("cannot open TPM");
120+
}
121+
return false;
122+
}
123+
124+
public boolean isReady() {
125+
CreatePrimaryResponse rsaEk = this.rsaEk; // to avoid warning
126+
return (tpm != null) && (rsaEk != null) && (rsaSrk != null) && (rsaEk.outPublic != null)
127+
&& (policySession != null);
128+
}
129+
98130
/**
99131
* Generate keys required for encryption and decryption.
100132
* As TPM uses a key derivation function to derive the key from an
101133
* internal seed set at production time, identical keys can be created.
102134
*
103135
* @throws SecurityException
104136
*/
105-
public void generateKeys() throws SecurityException {
137+
public synchronized void generateKeys() throws SecurityException {
138+
if ((rsaEk != null) && (rsaSrk != null)) {
139+
return; // keys already exist, re-creating will lead to same keys
140+
}
141+
init();
142+
Tpm tpm = this.tpm; // local copy to avoid Null warnings
143+
if (tpm == null) {
144+
throw new SecurityException("TPM cannot be opened");
145+
}
106146
try {
107147
Instant start = Instant.now();
108148
TPMT_PUBLIC rsaEkTemplate = new TPMT_PUBLIC(TPM_ALG_ID.SHA256,
@@ -137,7 +177,7 @@ STANDARD_EK_POLICY, new TPMS_RSA_PARMS(new TPMT_SYM_DEF_OBJECT(TPM_ALG_ID.AES, 1
137177
end = Instant.now();
138178
logger.debug("TPM based RSA storage key generated in {} seconds", Duration.between(start, end).toSeconds());
139179

140-
logger.info("TPM key genration complete");
180+
logger.info("TPM key generation complete");
141181
} catch (TpmException e) {
142182
throw new SecurityException("TPM exception", e);
143183
}
@@ -149,6 +189,11 @@ STANDARD_EK_POLICY, new TPMS_RSA_PARMS(new TPMT_SYM_DEF_OBJECT(TPM_ALG_ID.AES, 1
149189
* @throws SecurityException
150190
*/
151191
public SecuredPassword encryptSecret(String secret) throws SecurityException {
192+
init();
193+
Tpm tpm = this.tpm; // local copy to avoid Null warnings
194+
if (tpm == null) {
195+
throw new SecurityException("TPM cannot be opened");
196+
}
152197
try {
153198
if ((rsaEk == null) || (rsaSrk == null)) {
154199
generateKeys();
@@ -175,6 +220,11 @@ public SecuredPassword encryptSecret(String secret) throws SecurityException {
175220
}
176221

177222
public String encryptAndSerializeSecret(String secret) throws SecurityException {
223+
init();
224+
Tpm tpm = this.tpm; // local copy to avoid Null warnings
225+
if (tpm == null) {
226+
throw new SecurityException("TPM cannot be opened");
227+
}
178228
try {
179229
ByteArrayOutputStream stream = new ByteArrayOutputStream();
180230
ObjectOutputStream serial = new ObjectOutputStream(stream);
@@ -192,6 +242,11 @@ public String encryptAndSerializeSecret(String secret) throws SecurityException
192242
* @throws SecurityException
193243
*/
194244
public String decryptSecret(SecuredPassword secret) throws SecurityException {
245+
init();
246+
Tpm tpm = this.tpm; // local copy to avoid Null warnings
247+
if (tpm == null) {
248+
throw new SecurityException("TPM cannot be opened");
249+
}
195250
try {
196251
if ((rsaEk == null) || (rsaSrk == null)) {
197252
generateKeys();
@@ -211,14 +266,20 @@ public String decryptSecret(SecuredPassword secret) throws SecurityException {
211266

212267
// policy session
213268
byte[] nonceCaller = Helpers.RandomBytes(20);
214-
StartAuthSessionResponse policySession = tpm.StartAuthSession(TPM_HANDLE.NULL, TPM_HANDLE.NULL, nonceCaller,
215-
new byte[0], TPM_SE.POLICY, new TPMT_SYM_DEF(), TPM_ALG_ID.SHA256);
269+
if (policySession == null) {
270+
policySession = tpm.StartAuthSession(TPM_HANDLE.NULL, TPM_HANDLE.NULL, nonceCaller, new byte[0],
271+
TPM_SE.POLICY, new TPMT_SYM_DEF(), TPM_ALG_ID.SHA256);
272+
}
273+
StartAuthSessionResponse policySession = this.policySession; // local copy to avoid Null warnings
274+
if (policySession == null) {
275+
throw new SecurityException("TPM decryption failed, cannot create policy session");
276+
}
216277
// password is used during creation of key handles, so it needs to be set
217278
policySession.handle.AuthValue = USER_PWD.getBytes();
218279
tpm.PolicySecret(tpm._EndorsementHandle, policySession.handle, new byte[0], new byte[0], new byte[0], 0);
219280
byte[] policyDigest = tpm.PolicyGetDigest(policySession.handle);
220281
if (!Helpers.arraysAreEqual(policyDigest, STANDARD_EK_POLICY)) {
221-
throw new SecurityException("TPM decryption failed");
282+
throw new SecurityException("TPM decryption failed, policy mismatch");
222283
}
223284

224285
tpm._withSessions(TPM_HANDLE.pwSession(new byte[0]), policySession.handle);
@@ -232,6 +293,11 @@ public String decryptSecret(SecuredPassword secret) throws SecurityException {
232293
}
233294

234295
public String deserializeAndDectryptSecret(String encryptedSecret) throws SecurityException {
296+
init();
297+
Tpm tpm = this.tpm; // local copy to avoid Null warnings
298+
if (tpm == null) {
299+
throw new SecurityException("TPM cannot be opened");
300+
}
235301
try {
236302
byte[] array = Helpers.fromHex(encryptedSecret);
237303
ByteArrayInputStream stream = new ByteArrayInputStream(array);
@@ -252,7 +318,12 @@ public String deserializeAndDectryptSecret(String encryptedSecret) throws Securi
252318
* @param bytesRequested
253319
* @return array of random numbers
254320
*/
255-
byte[] getRandom(int bytesRequested) {
321+
byte[] getRandom(int bytesRequested) throws SecurityException {
322+
init();
323+
Tpm tpm = this.tpm; // local copy to avoid Null warnings
324+
if (tpm == null) {
325+
throw new SecurityException("TPM cannot be opened");
326+
}
256327
return tpm.GetRandom(bytesRequested);
257328
}
258329

@@ -261,6 +332,11 @@ byte[] getRandom(int bytesRequested) {
261332
* @throws SecurityException
262333
*/
263334
public String getTpmFirmwareVersion() throws SecurityException {
335+
init();
336+
Tpm tpm = this.tpm; // local copy to avoid Null warnings
337+
if (tpm == null) {
338+
throw new SecurityException("TPM cannot be opened");
339+
}
264340
try {
265341
int ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.FIRMWARE_VERSION_1);
266342
int major = ret >> 16;
@@ -276,6 +352,11 @@ public String getTpmFirmwareVersion() throws SecurityException {
276352
* @throws SecurityException
277353
*/
278354
public String getTpmManufacturerShort() throws SecurityException {
355+
init();
356+
Tpm tpm = this.tpm; // local copy to avoid Null warnings
357+
if (tpm == null) {
358+
throw new SecurityException("TPM cannot be opened");
359+
}
279360
try {
280361
StringBuilder sb = new StringBuilder(4);
281362
int ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.MANUFACTURER);
@@ -293,6 +374,11 @@ public String getTpmManufacturerShort() throws SecurityException {
293374
* @throws SecurityException
294375
*/
295376
public String getTpmModel() throws SecurityException {
377+
init();
378+
Tpm tpm = this.tpm; // local copy to avoid Null warnings
379+
if (tpm == null) {
380+
throw new SecurityException("TPM cannot be opened");
381+
}
296382
try {
297383
StringBuilder sb = new StringBuilder(24);
298384
int ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.VENDOR_STRING_1);
@@ -323,6 +409,11 @@ public String getTpmModel() throws SecurityException {
323409
* @throws SecurityException
324410
*/
325411
public String getTpmTcgLevel() throws SecurityException {
412+
init();
413+
Tpm tpm = this.tpm; // local copy to avoid Null warnings
414+
if (tpm == null) {
415+
throw new SecurityException("TPM cannot be opened");
416+
}
326417
try {
327418
int ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.LEVEL);
328419
return "" + ret;
@@ -337,6 +428,11 @@ public String getTpmTcgLevel() throws SecurityException {
337428
* @throws SecurityException
338429
*/
339430
public String getTpmTcgRevision() throws SecurityException {
431+
init();
432+
Tpm tpm = this.tpm; // local copy to avoid Null warnings
433+
if (tpm == null) {
434+
throw new SecurityException("TPM cannot be opened");
435+
}
340436
try {
341437
int ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.REVISION);
342438
return "" + (ret / 100) + "." + (ret % 100);
@@ -351,6 +447,11 @@ public String getTpmTcgRevision() throws SecurityException {
351447
* @throws SecurityException
352448
*/
353449
public String getTpmVersion() throws SecurityException {
450+
init();
451+
Tpm tpm = this.tpm; // local copy to avoid Null warnings
452+
if (tpm == null) {
453+
throw new SecurityException("TPM cannot be opened");
454+
}
354455
try {
355456
StringBuilder sb = new StringBuilder(4);
356457
int ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.FAMILY_INDICATOR);

0 commit comments

Comments
 (0)