Skip to content

Commit

Permalink
single instance
Browse files Browse the repository at this point in the history
Signed-off-by: Holger Friedrich <[email protected]>
  • Loading branch information
holgerfriedrich committed Aug 6, 2023
1 parent f68ef3f commit 2d18611
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,8 @@ protected String decrypt(String secret) {
if (secret.startsWith(KNXBindingConstants.ENCYRPTED_PASSWORD_SERIALIZATION_PREFIX)) {
try {
logger.info("trying to access TPM module");
if (tpmIf == null) {
tpmIf = new TpmInterface();
logger.info("generating keys, this might take some time");
}
TpmInterface tmpTpmIf = tpmIf;
if (tmpTpmIf != null) {
secret = tmpTpmIf.deserializeAndDectryptSecret(
secret.substring(KNXBindingConstants.ENCYRPTED_PASSWORD_SERIALIZATION_PREFIX.length()));
} else {
logger.error("Unable to decode stored password using TPM");
}
return TpmInterface.TPM.deserializeAndDectryptSecret(
secret.substring(KNXBindingConstants.ENCYRPTED_PASSWORD_SERIALIZATION_PREFIX.length()));
} catch (SecurityException e) {
logger.error("Unable to decode stored password using TPM: {}", e.getMessage());
// fall through
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,26 +68,28 @@ public void execute(String[] args, Console console) {
} else if (args.length == 1 && CMD_TPM_INFO.equalsIgnoreCase(args[0])) {
try {
console.println("trying to access TPM module");
TpmInterface tpm = new TpmInterface();
console.println("TPM version: " + tpm.getTpmVersion());
console.println("TPM model: " + tpm.getTpmManufacturerShort() + " " + tpm.getTpmModel());
console.println("TPM firmware: " + tpm.getTpmFirmwareVersion());
console.println("TPM TCG Spec.: rev. " + tpm.getTpmTcgRevision() + " level " + tpm.getTpmTcgLevel());
console.println("TPM version: " + TpmInterface.TPM.getTpmVersion());
console.println("TPM model: " + TpmInterface.TPM.getTpmManufacturerShort() + " "
+ TpmInterface.TPM.getTpmModel());
console.println("TPM firmware: " + TpmInterface.TPM.getTpmFirmwareVersion());
console.println("TPM TCG Spec.: rev. " + TpmInterface.TPM.getTpmTcgRevision() + " level "
+ TpmInterface.TPM.getTpmTcgLevel());
} catch (SecurityException e) {
console.print("error: " + e.getMessage());
}
return;
} else if (args.length == 2 && CMD_TPM_ENCRYPT.equalsIgnoreCase(args[0])) {
try {
console.println("trying to access TPM module");
TpmInterface tpm = new TpmInterface();
console.println("generating keys, this might take some time");
String p = tpm.encryptAndSerializeSecret(args[1]);
if (!TpmInterface.TPM.isReady()) {
console.println("generating keys, this might take some time");
}
String p = TpmInterface.TPM.encryptAndSerializeSecret(args[1]);
console.println("encrypted representation of password");
console.println(KNXBindingConstants.ENCYRPTED_PASSWORD_SERIALIZATION_PREFIX + p);

// check if TPM can decrypt
String decrypted = tpm.deserializeAndDectryptSecret(p);
String decrypted = TpmInterface.TPM.deserializeAndDectryptSecret(p);
if (args[1].equals(decrypted)) {
console.println("Password successfully recovered from encrypted representation");
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@
* @author Holger Friedrich - Initial contribution
*/
@NonNullByDefault
public class TpmInterface {
public enum TpmInterface {
TPM;

private final Logger logger = LoggerFactory.getLogger(TpmInterface.class);
private static final byte[] STANDARD_EK_POLICY = Helpers
.fromHex("837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa");
Expand All @@ -70,7 +72,8 @@ public class TpmInterface {

private @Nullable CreatePrimaryResponse rsaEk;
private @Nullable CreatePrimaryResponse rsaSrk;
private Tpm tpm;
private @Nullable Tpm tpm;
private @Nullable StartAuthSessionResponse policySession;

public record SecuredPassword(String secret, String encIdentity, String integrityHMAC) implements Serializable {
private static final long serialVersionUID = 238409238L;
Expand All @@ -81,28 +84,65 @@ public record SecuredPassword(String secret, String encIdentity, String integrit
*
* @throws SecurityException
*/
public TpmInterface() throws SecurityException {
try {
@Nullable
Tpm tmpTpm = TpmFactory.platformTpm();
if (tmpTpm == null) {
private TpmInterface() {
}

private void init() throws SecurityException {
if (tpm == null) {
initSynchronized();
}
}

private synchronized void initSynchronized() throws SecurityException {
if (tpm == null) {
try {
tpm = TpmFactory.platformTpm();
} catch (TpmException e) {
throw new SecurityException("TPM cannot be accessed", e);
}
if (tpm == null) {
throw new SecurityException("TPM cannot be accessed");
} else {
tpm = tmpTpm;
}
} catch (TpmException e) {
throw new SecurityException("TPM cannot be accessed", e);
}
}

public boolean isAvailable() {
if (tpm != null) {
return true;
}
try {
init();
if (tpm != null) {
return true;
}
} catch (SecurityException e) {
logger.info("cannot open TPM");
}
return false;
}

public boolean isReady() {
CreatePrimaryResponse rsaEk = this.rsaEk; // to avoid warning
return (tpm != null) && (rsaEk != null) && (rsaSrk != null) && (rsaEk.outPublic != null)
&& (policySession != null);
}

/**
* Generate keys required for encryption and decryption.
* As TPM uses a key derivation function to derive the key from an
* internal seed set at production time, identical keys can be created.
*
* @throws SecurityException
*/
public void generateKeys() throws SecurityException {
public synchronized void generateKeys() throws SecurityException {
if ((rsaEk != null) && (rsaSrk != null)) {
return; // keys already exist, re-creating will lead to same keys
}
init();
Tpm tpm = this.tpm; // local copy to avoid Null warnings
if (tpm == null) {
throw new SecurityException("TPM cannot be opened");
}
try {
Instant start = Instant.now();
TPMT_PUBLIC rsaEkTemplate = new TPMT_PUBLIC(TPM_ALG_ID.SHA256,
Expand Down Expand Up @@ -137,7 +177,7 @@ STANDARD_EK_POLICY, new TPMS_RSA_PARMS(new TPMT_SYM_DEF_OBJECT(TPM_ALG_ID.AES, 1
end = Instant.now();
logger.debug("TPM based RSA storage key generated in {} seconds", Duration.between(start, end).toSeconds());

logger.info("TPM key genration complete");
logger.info("TPM key generation complete");
} catch (TpmException e) {
throw new SecurityException("TPM exception", e);
}
Expand All @@ -149,6 +189,11 @@ STANDARD_EK_POLICY, new TPMS_RSA_PARMS(new TPMT_SYM_DEF_OBJECT(TPM_ALG_ID.AES, 1
* @throws SecurityException
*/
public SecuredPassword encryptSecret(String secret) throws SecurityException {
init();
Tpm tpm = this.tpm; // local copy to avoid Null warnings
if (tpm == null) {
throw new SecurityException("TPM cannot be opened");
}
try {
if ((rsaEk == null) || (rsaSrk == null)) {
generateKeys();
Expand All @@ -175,6 +220,11 @@ public SecuredPassword encryptSecret(String secret) throws SecurityException {
}

public String encryptAndSerializeSecret(String secret) throws SecurityException {
init();
Tpm tpm = this.tpm; // local copy to avoid Null warnings
if (tpm == null) {
throw new SecurityException("TPM cannot be opened");
}
try {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ObjectOutputStream serial = new ObjectOutputStream(stream);
Expand All @@ -192,6 +242,11 @@ public String encryptAndSerializeSecret(String secret) throws SecurityException
* @throws SecurityException
*/
public String decryptSecret(SecuredPassword secret) throws SecurityException {
init();
Tpm tpm = this.tpm; // local copy to avoid Null warnings
if (tpm == null) {
throw new SecurityException("TPM cannot be opened");
}
try {
if ((rsaEk == null) || (rsaSrk == null)) {
generateKeys();
Expand All @@ -211,14 +266,20 @@ public String decryptSecret(SecuredPassword secret) throws SecurityException {

// policy session
byte[] nonceCaller = Helpers.RandomBytes(20);
StartAuthSessionResponse policySession = tpm.StartAuthSession(TPM_HANDLE.NULL, TPM_HANDLE.NULL, nonceCaller,
new byte[0], TPM_SE.POLICY, new TPMT_SYM_DEF(), TPM_ALG_ID.SHA256);
if (policySession == null) {
policySession = tpm.StartAuthSession(TPM_HANDLE.NULL, TPM_HANDLE.NULL, nonceCaller, new byte[0],
TPM_SE.POLICY, new TPMT_SYM_DEF(), TPM_ALG_ID.SHA256);
}
StartAuthSessionResponse policySession = this.policySession; // local copy to avoid Null warnings
if (policySession == null) {
throw new SecurityException("TPM decryption failed, cannot create policy session");
}
// password is used during creation of key handles, so it needs to be set
policySession.handle.AuthValue = USER_PWD.getBytes();
tpm.PolicySecret(tpm._EndorsementHandle, policySession.handle, new byte[0], new byte[0], new byte[0], 0);
byte[] policyDigest = tpm.PolicyGetDigest(policySession.handle);
if (!Helpers.arraysAreEqual(policyDigest, STANDARD_EK_POLICY)) {
throw new SecurityException("TPM decryption failed");
throw new SecurityException("TPM decryption failed, policy mismatch");
}

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

public String deserializeAndDectryptSecret(String encryptedSecret) throws SecurityException {
init();
Tpm tpm = this.tpm; // local copy to avoid Null warnings
if (tpm == null) {
throw new SecurityException("TPM cannot be opened");
}
try {
byte[] array = Helpers.fromHex(encryptedSecret);
ByteArrayInputStream stream = new ByteArrayInputStream(array);
Expand All @@ -252,7 +318,12 @@ public String deserializeAndDectryptSecret(String encryptedSecret) throws Securi
* @param bytesRequested
* @return array of random numbers
*/
byte[] getRandom(int bytesRequested) {
byte[] getRandom(int bytesRequested) throws SecurityException {
init();
Tpm tpm = this.tpm; // local copy to avoid Null warnings
if (tpm == null) {
throw new SecurityException("TPM cannot be opened");
}
return tpm.GetRandom(bytesRequested);
}

Expand All @@ -261,6 +332,11 @@ byte[] getRandom(int bytesRequested) {
* @throws SecurityException
*/
public String getTpmFirmwareVersion() throws SecurityException {
init();
Tpm tpm = this.tpm; // local copy to avoid Null warnings
if (tpm == null) {
throw new SecurityException("TPM cannot be opened");
}
try {
int ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.FIRMWARE_VERSION_1);
int major = ret >> 16;
Expand All @@ -276,6 +352,11 @@ public String getTpmFirmwareVersion() throws SecurityException {
* @throws SecurityException
*/
public String getTpmManufacturerShort() throws SecurityException {
init();
Tpm tpm = this.tpm; // local copy to avoid Null warnings
if (tpm == null) {
throw new SecurityException("TPM cannot be opened");
}
try {
StringBuilder sb = new StringBuilder(4);
int ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.MANUFACTURER);
Expand All @@ -293,6 +374,11 @@ public String getTpmManufacturerShort() throws SecurityException {
* @throws SecurityException
*/
public String getTpmModel() throws SecurityException {
init();
Tpm tpm = this.tpm; // local copy to avoid Null warnings
if (tpm == null) {
throw new SecurityException("TPM cannot be opened");
}
try {
StringBuilder sb = new StringBuilder(24);
int ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.VENDOR_STRING_1);
Expand Down Expand Up @@ -323,6 +409,11 @@ public String getTpmModel() throws SecurityException {
* @throws SecurityException
*/
public String getTpmTcgLevel() throws SecurityException {
init();
Tpm tpm = this.tpm; // local copy to avoid Null warnings
if (tpm == null) {
throw new SecurityException("TPM cannot be opened");
}
try {
int ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.LEVEL);
return "" + ret;
Expand All @@ -337,6 +428,11 @@ public String getTpmTcgLevel() throws SecurityException {
* @throws SecurityException
*/
public String getTpmTcgRevision() throws SecurityException {
init();
Tpm tpm = this.tpm; // local copy to avoid Null warnings
if (tpm == null) {
throw new SecurityException("TPM cannot be opened");
}
try {
int ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.REVISION);
return "" + (ret / 100) + "." + (ret % 100);
Expand All @@ -351,6 +447,11 @@ public String getTpmTcgRevision() throws SecurityException {
* @throws SecurityException
*/
public String getTpmVersion() throws SecurityException {
init();
Tpm tpm = this.tpm; // local copy to avoid Null warnings
if (tpm == null) {
throw new SecurityException("TPM cannot be opened");
}
try {
StringBuilder sb = new StringBuilder(4);
int ret = TpmHelpers.getTpmProperty(tpm, TPM_PT.FAMILY_INDICATOR);
Expand Down
Loading

0 comments on commit 2d18611

Please sign in to comment.