Hello Ramon,
In my view, there are pros and cons for each of these solutions. Choosing the best design depends on the exact platform requirements.
Given that BL1 cannot be updated on your platform, you probably want to keep it as simple as possible to reduce the risk of introducing unpatchable bugs. If BL1 is responsible for measuring BL2 and writing this measurement into the I2C TPM, obviously it needs the measurement logic as well as some (potentially simplified) TPM driver. This might be undesirable, depending on how complex the TPM driver would be. -> This is solution 1 (BL1 measures BL2 and records the measurement in the TPM).
An alternate design, as you say, is to measure BL2 from BL1, but initially store the measurement in secure RAM. Later, some other, updatable software component (e.g. BL2) can pick it up and store it in the TPM on behalf of BL1.
This removes the need to have a TPM driver in BL1. However, this is theoretically a bit less secure than solution 1 because it is conceptually easier to tamper with secure RAM than with the TPM, which is a separate chip dedicated to protect this kind of data. That being said, the window of time for which the measurement is kept in secure RAM before being securely stored in TPM might be small enough that the risk of someone tampering with it is acceptable. -> This is solution 2 (BL1 just measures BL2, BL2 records its own measurement).
Another option is for BL2 to self-measure itself (and store its own measurement to the TPM). This means BL1 does not even need the measurement capability. However, for this design to be secure, trust needs to be established in BL2, i.e. we need to be sure that it's not been tampered with and that we can trust it will correctly take the measurements and record them.
Typically this means you need trusted boot support in BL1 in order to verify the authenticity of BL2 image (unless there is some other way to establish this trust, perhaps delegating the authentication to some other entity). If you were not planning to enable trusted boot in BL1 then this is pulling extra complexity in BL1 anyway.
Also, the self-measurement needs to happen early enough in BL2, before any change is made to BL2 global data. Else, what you measure will not be BL2 initial state, but rather some early state of BL2. If this state is non-deterministic (think about the initialization of a canary with random value for stack smashing protection) then this makes it impossible to get a measurement which you can use for attestation purposes. -> This is solution 3 (BL2 self-measures itself).
To me, solution 3 sounds like the most complex one (but I thought it would be good to mention it in case it suits your use case). Which of solution 1 or 2 is best really depends on your requirements. In any case, it should be possible to implement any of these solutions with the current TF-A code base. It has been designed with the intent to leave as much freedom as possible to platform implementations. Effectively the only expectation is that the platform layer should provide 3 hooks - init, measure and exit - in BL1 and in BL2. If, say, you wanted to go with solution 3 then all 3 hooks for BL1 would do nothing.
I hope that helps.
Regards, Sandrine
On 5/30/22 12:15, Ramon Fried wrote:
Hi, I'm currently working on porting TFA to our upcoming SOC. We plan to support Measured boot using external I2C TPM module. I'm wondering about the implementation of that in BL1. Do you think that I need to write the measurements directly to the I2C module in BL1 ? I'm asking because I would like to have the least source of problems in BL1 which I can't upgrade. I thought of storing the measurements in secure RAM and perhaps copy later. Would love to hear your thoughts.
PS. Actually I would love to have the option to choose to implement TPM also in SW (fTPM using optee - as was done in the POC). I think that if I store the measurement of BL2 in secure RAM I can later change the specific TPM while upgrading only BL2/BL31...
Thanks, Ramon