[SLOF] [PATCH v6 6/7] tcgbios: Add TPM 2.0 support and firmware API

Stefan Berger stefanb at linux.ibm.com
Thu Jan 23 01:35:56 AEDT 2020


On 1/21/20 9:24 PM, Alexey Kardashevskiy wrote:
>
> On 21/01/2020 04:08, Stefan Berger wrote:
>> On 1/20/20 3:09 AM, Alexey Kardashevskiy wrote:
>>> On 16/01/2020 07:00, Stefan Berger wrote:
>>>> This patch adds TPM 2.0 support along with the firmware API
>>>> that Linux uses to transfer the firmware log.
>>>>
>>>> Signed-off-by: Stefan Berger <stefanb at linux.ibm.com>
>>>>
>>>> +;
>>>> +
>>>> +\ forward a call to /ibm,vtpm, which implements the function with the
>>>> +\ given name
>>>> +: vtpm-call-forward ( arg ... arg name namelen -- ret ... ret
>>>> failure? )
>>>> +    \ assign /ibm,vtpm node to vtpm-ihandle, if not assigned
>>>> +    vtpm-ihandle 0= IF
>>>> +        s" /ibm,vtpm" open-dev to vtpm-ihandle
>>> Why does not "open" do this? Is this vtpm supposed to run even before
>>> the client tries using the vtpm services? It does not look like it.
>> Initialization of the vTPM is supposed to happen before any client can
>> talk to the firmware.
>
> And how doing it in "open" breaks this rule? If the client does not open
> the node, it cannot use the vtpm.


The firmware is supposed to take measurements while it is running. 
Before it can do that it needs to initialize the TPM, so that's why we 
are doing this without any higher level client coming in through the 
API. Further, when the user configures the TPM via the menu the device 
has to have been initialized already.


>
>
>
>>>
>>>> +    THEN
>>>> +
>>>> +    vtpm-ihandle 0<> IF
>>>> +        vtpm-ihandle                   ( arg ... arg name namelen
>>>> ihandle )
>>>> +        $call-method                   ( ret ... ret )
>>>> +        false                          ( ret ... ret false )
>>>> +    ELSE
>>>> +        true                           ( true )
>>>> +    THEN
>>>> +;
>>>> +
>>>> +\ firmware API call
>>>> +: sml-get-allocated-size ( -- buffer-size)
>>>> +    " sml-get-allocated-size" vtpm-call-forward IF
>>>> +        \ vtpm-call-forward failed
>>>> +        0
>>>> +    THEN
>>>> +;
>>>> +
>>>> +\ firmware API call
>>>> +: sml-get-handover-size ( -- size)
>>>> +    " sml-get-handover-size" vtpm-call-forward IF
>>>> +        \ vtpm-call-forward failed
>>>> +        0
>>>> +    THEN
>>>> +;
>>>> +
>>>> +\ firmware API call
>>>> +: sml-handover ( dest size -- )
>>>> +    " sml-handover" vtpm-call-forward IF
>>>> +        \ vtpm-call-forward failed; clean up stack
>>>> +        2drop
>>>> +    THEN
>>>> +;
>>>> +
>>>> +\ firmware API call
>>>> +: get-failure-reason ( -- reason )
>>>> +    " get-failure-reason" vtpm-call-forward IF
>>>> +        \ vtpm-call-forward failed; return a value
>>>> +        0 \ invalid
>>>> +    THEN
>>>> +;
>>>> +
>>>> +0 0 s" ibm,sml-efi-reformat-supported" property
>>>> +
>>>> +\ firmware API call
>>>> +: reformat-sml-to-efi-alignment ( -- success )
>>>> +    " reformat-sml-to-efi-alignment" vtpm-call-forward IF
>>>> +        false
>>>> +    THEN
>>>> +;
>>>> +
>>>> +: open ( )
>>>> +    vtpm-debug? IF ." VTPM: vTPM open()" cr THEN
>>>> +    true
>>>> +;
>>>> +
>>>> +: close ( )
>>>> +    vtpm-debug? IF ." VTPM: vTPM close()" cr THEN
>>>> +;
>>>> +
>>>> +\ setup alias and the RTAS bypass
>>>> +vtpm-init
>>>> +
>>>> +\ setup the log
>>>> +include vtpm-sml.fs
>>>> +
>>>> +s" /ibm,vtpm" find-node ?dup IF
>>>> +  s" measure-scrtm" rot $call-static
>>>> +THEN
>>> The above 22 lines confuse me a lot.
>>> Why vtpm-sml.fs?
>> You mean why does vtpm-sml.fs exist at all?Or why not just put all vTPM
>> code into one file?
> Yes.

Have a look at v7. I would agree to switching the names of the files now 
as things have moved around, but keeping the code in different files 
seems to make it 'cleaner' -- at least to my eyes. As said, I do have 
some challenges with the Forth code and it's not clear to me what is the 
better code. Back then when I wrote this I derived it from from the 
vscsi code, which still does things similar today:

https://github.com/stefanberger/SLOF-tpm/blob/master/board-qemu/slof/vio-vscsi.fs#L534


vtpm-init is similar to vscsi-init-and-scan. Right or wrong, it couldn't 
tell, but at least derived from some existing code that does it similarly.


>> "PFW Virtual TPM Driver" Version 1.1 (or later)
>
> Put this to the commit log.


See v7: https://lists.ozlabs.org/pipermail/slof/2020-January/002506.html


>> The title of the document is 'PFW Virtual TPM Driver'.
>> PFW_CTPM_CLDD_1.1-1.pdf is the file I have.
>>
> Cool, save it in the comment or commit log.


v7 got it.


>
>
>>>
>>>> and the TCG Specification
>>>> + *  that can be found here under the following link:
>>>> + *
>>>> http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific_implementation_specification_for_conventional_bios
>>>>
>>> The link is broken.
>>
>> https://trustedcomputinggroup.org/resource/pc-client-work-group-specific-implementation-specification-for-conventional-bios/
>
> and this one.


Updated the link in the file.


>>>> +        .hash_buffersize = SHA1_BUFSIZE,
>>>> +    }, {
>>>> +        .hashalg = TPM2_ALG_SHA256,
>>>> +        .hash_buffersize = SHA256_BUFSIZE,
>>>> +    }, {
>>>> +        .hashalg = TPM2_ALG_SHA384,
>>>> +        .hash_buffersize = SHA384_BUFSIZE,
>>>> +    }, {
>>>> +        .hashalg = TPM2_ALG_SHA512,
>>>> +        .hash_buffersize = SHA512_BUFSIZE,
>>>> +    }, {
>>>> +        .hashalg = TPM2_ALG_SM3_256,
>>>> +        .hash_buffersize = SM3_256_BUFSIZE,
>>>> +    }
>>>> +};
>>>> +
>>>> +static int
>>>> +tpm20_get_hash_buffersize(uint16_t hashAlg)
>>>> +{
>>>> +    unsigned i;
>>>> +
>>>> +    for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
>>>> +        if (hash_parameters[i].hashalg == hashAlg)
>>>> +            return hash_parameters[i].hash_buffersize;
>>>> +    }
>>>> +    return -1;
>>> Just make it return (const struct hash_parameters *) as 7/7 adds one
>>> helper like this for every field of hash_parameters.
>>
>> OK.
>>
>>
>>>
>>>> +}
>>>> +
>>>> +/*
>>>> + * Build the TPM2 tpm2_digest_values data structure from the given
>>>> hash.
>>>> + * Follow the PCR bank configuration of the TPM and write the same hash
>>>> + * in either truncated or zero-padded form in the areas of all the
>>>> other
>>>> + * hashes. For example, write the sha1 hash in the area of the sha256
>>>> + * hash and fill the remaining bytes with zeros. Or truncate the sha256
>>>> + * hash when writing it in the area of the sha1 hash.
>>> It must be correct since you did this but what is the point of this? If
>>> the digest does not fit, fill it with 0xBAADF00D and return an error?
>> A TPM 2 has typically 24 platform control registers (PCRs). These
>> registers are storing each a hash (rather than an integer). Basically
>> there is one operation on a hash, which is an 'extend':
>>
>> pcr1 = sha256(pcr1 || hash)
>>
>> So it takes the current value of PCR 1 and concatenates it with the
>> 'hash' to extend it and hashes this concatenation and assigns the result
>> to PCR 1.
>>
>> A TPM 2 may have several hash banks, such as for sha1, sha256, sha384,
>> sha512, sm3-256 etc.  So there may be multiple PCR 1 registers, one for
>> the sha1 bank, one for the sha256 bank etc.
>>
>> For the logging of measurements we are only using one hash, that is
>> sha256 now, and extend this in PCR1 of sha1, sha256, etc. We do this for
>> all the 'active' PCR banks basically.
> Do we have to do this for all active PCR banks? Sorry for stupid
> questions :)

Page 96 in pdf "TCG PC Client Platform Firmware Profile"

https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf

There SHALL be a Hash algorithm in the TCG  PCClientPCREvent structure 
for  all allocated PCR banks.

There MAY be Hash algorithms in the TCG_PCClientPCREvent structure that 
do not correspond to allocated PCR banks.  See Section 11 Predictive 
Event Logs.


We chose to do both in SeaBIOS. From what I am hearing other firmwares 
may do the same and follow the 'MAY'.


>
>> We log the 'hash' value that a PCR
>> was extended so that someone looking at the current PCR value can use
>> the log to replay (simulate) the extensions of the PCRs. Since we are
>> now using sha256 for hashing we will need to truncate the sha256 when
>> logging it for the sha1 bank and zero pad it for sha384 since sha384 has
>> more bytes. There is the integrity measurement architecture (IMA) in
>> Linux that basically does the same thing:
> Something is missing after ":"?

IMA takes one measurement of a file and extends all active PCR banks 
with this hash. Depending on the algorithm used for hashing versus the 
hash algorithm of a PCR bank a hash may be trucated, fit exactly, or 
zero-padded at the end

https://elixir.bootlin.com/linux/latest/source/security/integrity/ima/ima_queue.c#L147

later on size-adjusted: 
https://elixir.bootlin.com/linux/latest/source/drivers/char/tpm/tpm2-cmd.c#L275



>
>
>>
>>
>>>
>>>> + *
>>>> + * le: the log entry to build the digest in
>>>> + * sha1: the sha1 hash value to use
>>>> + * bigEndian: whether to build in big endian format for the TPM or log
>>>> + *            little endian for the log (TPM 2.0)
>>>> + *
>>>> + * Returns the digest size; -1 on fatal error
>>>> + */
>>>> +static int tpm20_build_digest(struct tpm_log_entry *le, const
>>>> uint8_t *sha256,
>>>> +                  bool bigEndian)
>>> Is not it always little endian as it is v2.0 only?
>> There are callers for this function who want to send the resulting data
>> structures to the TPM. Other callers want this for the log written in
>> little endian.
>
> The function prototype suggests it is always for the log which is always
> little endian as it is v2.0 only.


This is what the parameter description says. The function can be called 
for different purposes.

* bigEndian: whether to build in big endian format for the TPM or log
*            little endian for the log (TPM 2.0)

We may need to send the result of this call to the TPM (TPM 2.0 that is) and it takes the data in big endian.


The little endian use case is for writing into the log. This is from "TCG PC Client Platfrom Firmware Profile" (pdf page 92):

https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf

"Note that for the digests in the crypto agile log format, although the type names from the  TPM  2.0  Library  Specification  are  used(e.g.  TPML_DIGEST_VALUES),  the storage of Integers in the event log is little-endian. In this case, the Count and the AlgID fields, leveraged from the TPM 2.0 Library specification,are stored as little-endian. If Platform Firmware utilizes the TPM2_PCR_Event command to hash the data,  this  command  returns  a  TPML_DIGEST_VALUES  structure  encoded  in  big-endian.    Platform Firmware  would  need  to  modify  the  TPML_DIGEST_VALUES structure so that count and AlgID fields are little-endian before adding the event to 2655the    event    log.        Likewise,    if Platform Firmware    is    preparing    the TPML_DIGEST_VALUES structure to send to the TPM using  a TPM2_PCR_Extend command, the values must be big-endian for the TPM structure.  There may be other instances  where  this  type  of  re-encoding  is  required.  This  specification  does  not produce an exhaustive list."




More information about the SLOF mailing list