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