Azure / azure-iot-sdk-java

A Java SDK for connecting devices to Microsoft Azure IoT services
https://azure.github.io/azure-iot-sdk-java/
Other
199 stars 236 forks source link

[Bug Report] Connect to IoT hub via TPM after successfull provisioning #1632

Closed marosrojis closed 1 year ago

marosrojis commented 1 year ago

Context

Description of the issue

When a device is successfully provisioned using Device Provisioning Service and TPM, there is a problem with connecting to the IoT Hub using provided IoT Hub and device ID (DPS response).

If I use a sample code, the provisioning and connecting to the IoT hub is successful. There is a problem when I want to connect to the IoT hub without reprovisioning. Based on this sample code I would like to save the IoT hub URI and Device ID to the cache and read them every time e.g. when the device is restarted.

But because I don't run reprovisioning and I would like to connect to the IoT hub using IoT Hub URI, device ID, and TPM. There is a problem the attribute SecurityProviderTPM.idKeyPub is null. The attribute is used only to get the hash algorithm, it is not used to generate a SAS token. The sample code works because the attribute SecurityProviderTPM.idKeyPub is filled during the provisioning process.

A similar problem is described here - https://stackoverflow.com/questions/59793319/connecting-to-azure-iot-hub-using-tpm?rq=1. There is a suggestion that works.

I already discussed the problem here.

Code sample exhibiting the issue

String iotHubUri = "fcr-iothub-test.azure-devices.net";
String deviceId = "test-device-1";
SecurityProviderTpm securityClient = new SecurityProviderTPMEmulator();

deviceClient = new DeviceClient(iotHubUri, deviceId, securityClient, IotHubClientProtocol.HTTPS);

Console log of the issue

2022-11-07 07:22:17,457 INFO (main) [com.microsoft.azure.sdk.iot.provisioning.security.hsm.SecurityProviderTPMEmulator] - Successfully read EK from TPM without creating a new one
2022-11-07 07:22:17,547 INFO (main) [com.microsoft.azure.sdk.iot.provisioning.security.hsm.SecurityProviderTPMEmulator] - Successfully read SRK from TPM without creating a new one
2022-11-07 07:22:17,555 INFO (main) [com.microsoft.azure.sdk.iot.device.transport.ExponentialBackoffWithJitter] - NOTE: A new instance of ExponentialBackoffWithJitter has been created with the following properties. Retry Count: 2147483647, Min Backoff Interval: 100, Max Backoff Interval: 10000, Max Time Between Retries: 100, Fast Retry Enabled: true
java.io.IOException: com.microsoft.azure.sdk.iot.provisioning.security.exceptions.SecurityProviderException: activateIdentityKey first before signing
    at com.microsoft.azure.sdk.iot.device.auth.IotHubSasTokenHardwareAuthenticationProvider.generateSasTokenSignatureFromSecurityProvider(IotHubSasTokenHardwareAuthenticationProvider.java:112)
    at com.microsoft.azure.sdk.iot.device.auth.IotHubSasTokenHardwareAuthenticationProvider.<init>(IotHubSasTokenHardwareAuthenticationProvider.java:49)
    at com.microsoft.azure.sdk.iot.device.ClientConfiguration.<init>(ClientConfiguration.java:315)
    at com.microsoft.azure.sdk.iot.device.ClientConfiguration.<init>(ClientConfiguration.java:354)
    at com.microsoft.azure.sdk.iot.device.InternalClient.<init>(InternalClient.java:83)
    at com.microsoft.azure.sdk.iot.device.DeviceClient.<init>(DeviceClient.java:143)
    at com.microsoft.azure.sdk.iot.device.DeviceClient.<init>(DeviceClient.java:128)
    at samples.com.microsoft.azure.sdk.iot.ProvisioningTpmSampleWithoutProvision.main(ProvisioningTpmSampleWithoutProvision.java:104)
Caused by: com.microsoft.azure.sdk.iot.provisioning.security.exceptions.SecurityProviderException: activateIdentityKey first before signing
    at com.microsoft.azure.sdk.iot.provisioning.security.hsm.SecurityProviderTPMEmulator.signWithIdentity(SecurityProviderTPMEmulator.java:505)
    at com.microsoft.azure.sdk.iot.device.auth.IotHubSasTokenHardwareAuthenticationProvider.generateSasTokenSignatureFromSecurityProvider(IotHubSasTokenHardwareAuthenticationProvider.java:100)
    ... 7 more
Shutting down...

Process finished with exit code 0

The idKeyPub is used only to get the hash algorithm (SHA-256), that's all.

private byte[] signData(Tpm tpm, TPMT_PUBLIC idKeyPub, byte[] tokenData) throws SecurityProviderException
    {
        TPM_ALG_ID  idKeyHashAlg = ((TPMS_SCHEME_HMAC)((TPMS_KEYEDHASH_PARMS)idKeyPub.parameters).scheme).hashAlg;
ngastelum-ms commented 1 year ago

Hi, in order to reconnect without reprovisioning you need to use the same security provider instance that you created when provisioning the device. The security client stores the authentication key from the service during the provisioning process. You can read more about TPM authentication via DPS here: https://learn.microsoft.com/azure/iot-dps/concepts-tpm-attestation#detailed-attestation-process.

We're also analyzing whether we should expose the authentication key to the user so that you could call activateIdentityKey with a new security provider instance by yourself.

marosrojis commented 1 year ago

Hi, thank you for your response. It is not possible to use the same instance of security provider when the IoT device is restarted. The interesting thing, you don't need to have activateIdentityKey in the security provider. The security provider uses activateIdentityKey only to get hash algorithm during generating SAS token, nothing else (based on the stackoverflow. The activateIdentityKey has to be only in the TPM.

And if you look at sample code to reprovisioning, there is no activateIdentityKey too. There is only IoT Hub name and device ID, nothing else.

Thank you for your help.

timtay-microsoft commented 1 year ago

After some discussion with the DPS service team, they have notified us that they don't encourage this kind of behavior for TPM since managing the state of the TPM is paramount for security purposes. For people who use TPM, the expectation will be that they reprovision each time the device is rebooted. To avoid this, we highly encourage using x509 attestation instead. Not only does it allow the kind of caching that is needed to avoid reprovisioning, but it is a more secure way for your device to connect.

Because of the above, I'm closing this thread as "won't fix/support"