joholl / rpi4-uboot-tpm

How to use a TPM in U-Boot on Raspberry Pi 4
Apache License 2.0
82 stars 16 forks source link

how to add and verify the tpm2.0 nv commands in the u-boot #12

Closed saravanj24 closed 2 years ago

saravanj24 commented 2 years ago

Hi,

We tried tpm2.0 with u-boot in the raspberry pi4, We are observe the error when run the nv_define command in u-boot. can you tell how to add and verify the nv command in u-boot.


CC @tkmozhi

joholl commented 2 years ago

Hi, have a look at that link where I patched u-boot to add a TPM command: https://github.com/joholl/rpi4-uboot-tpm/issues/1#issuecomment-694898768

Please also consider submitting your change upstream ;)

joholl commented 2 years ago

Btw, mainline u-boot already supports NV define/write/read: https://source.denx.de/u-boot/u-boot/-/blob/master/lib/tpm-v2.c

You just cannot read/write with policies or authValues (= passwords). The latter could be added easily if that's what you need.

saravanj24 commented 2 years ago

The below things we tried in the user space for the nvdefine and nv write/read

1.tpm2_nvdefine 0x1500030 -C 0x40000001 -s 10 -a 0x2000A
2.tpm2_nvwrite 0x1500030 -i - < inputdata 3.tpm2_nvread 0x1500030

with the above steps we can able do the read/write succesfully.

We undefine the nvindex created.

We tried the same above mentioned steps in the u-boot.

We created the do_tpm2_nv_define() to get command as the input to define the nv_index.

  1. we gave this command for define the nv_index : tpm2 nv_define 0x1500030 32 0x2000A
  2. we defined the nv-policy as NULL in the do_tpm2_nv_define() function.
  3. while executing the do_tpm2_nv_define() --> tpm2_nv_define_space(),it responded with the error

    TPM response [ret:725]: 80 01 00 00 00 0a 00 00 02 d5 Error: 725

Queries: 1.Is there any specific nv index we should use in the U-boot? 2.We assigned the owner read/owner write/Policy write in the nv_attributes to define the nv_index? Is there any attributes we need to set? 3.We also tried by setting nv_attributes as owner read/owner write alone, still we are getting same error response as 725. 4.We unable to find the error code details in TPM2.0 source package and also in related docs,suggest where we need to refer this? 5.Is there any other functionalities to be handle to define the nv_index?

Meanwhile we also tried with the which already available in the TPM U-boot package.

we checked with the early_nvram2 command, it failed during nv_write callback with the below error.

U-Boot> tpmtest early_nvram2 argc = 2, argv = tpmtest early_nvram2

Testing earlynavram2...TESTFAILED:line122:tpm_nv_write_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)): 0x284

Is there any update in the test commands for TPM2.0? is there we need to pass any arguments for this test command? is there any reference document available?

joholl commented 2 years ago

TL;DR: Either your do_tpm2_nv_define() has a bug or you pass bad values.

I broke the TPM2_NV_DefineSpace command down to the bytes as an example for you.

header:
  .tag: 80 02
  .size: 00 00 00 2d
  .commandCode: 00 00 01 2a

handle: 40 00 00 0c # platform

authArea:
  .size: 00 00 00 09
  .sessionHandle: 40 00 00 09 # password session
  .nonce: 00 00               # empty (size = 0)
  .attributes: 00
  .sessionAuth: 00 00         # empty (size = 0)

auth: 00 00
publicInfo:
  .size: 00 0e
  .nvPublic:
    nvPublic.nvIndex: 01 40 00 01    # arbitrary free NV index
    nvPublic.nameAlg: 00 04          # SHA1
    nvPublic.attributes: 44 04 20 04 # attributes, here TPMA_NV_AUTHREAD | TPMA_NV_POLICYWRITE | TPMA_NV_PLATFORMCREATE
    nvPublic.authPolicy: 00 00       # empty policy (size = 0)
    nvPublic.dataSize: 00 22         # size of the NV space: 34 bytes

You can verify this by sending these raw bytes to your TPM (or a TPM simulator, here mssim = Microsoft Simulator):

echo "80 02 00 00 00 2d 00 00 01 2a 40 00 00 0c 00 00 00 09 40 00 00 09 00 00 00 00 00 00 00 00 0e 01 40 00 01 00 04 44 04 20 04 00 00 00 22" | xxd -r -p | tpm2_send --tcti="mssim" | xxd -p

We created the do_tpm2_nv_define() to get command as the input to define the nv_index.

There is already an API call for defining an NV space in mainline U-Boot. You might want to use that.


1.Is there any specific nv index we should use in the U-boot?

Have a look at the reserved indices. Just use something in the unassigned range: 0x01000000-0x013FFFFF.


2.We assigned the owner read/owner write/Policy write in the nv_attributes to define the nv_index? Is there any attributes we need to set?

You're basically setting access control options here. That should not impact defining the space. Please refer to the specification for more info.


3.We also tried by setting nv_attributes as owner read/owner write alone, still we are getting same error response as 725.

See above.


4.We unable to find the error code details in TPM2.0 source package and also in related docs,suggest where we need to refer this?

You can use the tpm2-tools for info about return codes:

$ tpm2 rc_decode 0x02d5
tpm:parameter(2):structure is the wrong size

Basically, that error code is TPM2_RC_P (0x40) | TPM2_RC_SIZE (0x95)

From the TSS definitions:

#define TPM2_RC_FMT1 ((TPM2_RC) 0x080)  /* This bit is SET in all format 1 response codes. The codes in this group may have a value added to them to indicate the handle session or parameter to which they apply. */
#define TPM2_RC_P    ((TPM2_RC) 0x040) /* add to a parameter-related error */
#define TPM2_RC_SIZE ((TPM2_RC) (TPM2_RC_FMT1 + 0x015)) /* structure is the wrong size */

This typically means that at least one of the command parameters is malformed.


5.Is there any other functionalities to be handle to define the nv_index?

Nope, see the example at the top.

saravanj24 commented 2 years ago

I broke the TPM2_NV_DefineSpace command down to the bytes as an example for you.

We assign the below values for the nv_define API in U-Boot as given in the example.

We observed the below response (Error 256)

U-Boot> tpm2 nv_define
tpm_tis_spi_probe: gpio-reset is deprecated
Input values size:34 index:0x1400001 attribute:0x44042004
Input to "tpm2_nv_define_space" function values size:34 index:0x1400001 attribute:0x44042004
TPM response [ret:256]: 80 01 00 00 00 0a 00 00 01 00
Error: 256

We also tried nv_define with initialisation.. Observed same the Error:725

U-Boot> tpm2 init
tpm_tis_spi_probe: gpio-reset is deprecated
U-Boot> tpm2 startup TPM2_SU_CLEAR
TPM response [ret:0]: 80 01 00 00 00 0a 00 00 00 00
U-Boot> tpm2 nv_define
Input values size:34 index:0x1400001 attribute:0x44042004
Input to "tpm2_nv_define_space" function values size:34 index:0x1400001 attribute:0x44042004
TPM response [ret:725]: 80 01 00 00 00 0a 00 00 02 d5
Error: 725

You can verify this by sending these raw bytes to your TPM (or a TPM simulator, here mssim = Microsoft Simulator): echo "80 02 00 00 00 2d 00 00 01 2a 40 00 00 0c 00 00 00 09 40 00 00 09 00 00 00 00 00 00 00 00 0e 01 40 00 01 00 04 44 04 20 04 00 00 00 22" | xxd -r -p | tpm2_send --tcti="device:/dev/tpm0" | xxd -p

we observed the below as response: 80020000001300000000000000000000010000. What does this signify?

joholl commented 2 years ago

Success, basically.

header:
  .tag: 80 02
  .size: 00 00 00 13
  .responseCode: 00 00 00 00 # success

responseParamSize: 00 00 00 00

authArea:
  .nonce: 00 00        # empty (size = 0)
  .attributes: 01      # continue session
  .sessionAuth: 00 00  # empty (size = 0)

We also tried nv_define with initialisation.. Observed same the Error:725

Please provide me with the raw command bytes you send to the TPM for nv_define. As mentioned earlier, the TPM reports that a command parameter is invalid.

saravanj24 commented 2 years ago

Please provide me with the raw command bytes you send to the TPM for nv_define

We printed the command_v2 buffer on lib/tpm-v2.c and got the below response:

U-Boot> tpm2 init
tpm_tis_spi_probe: gpio-reset is deprecated
U-Boot> tpm2 startup TPM2_SU_CLEAR
Rseponse bfr 988989664TPM response [ret:0]: 80 01 00 00 00 0a 00 00 00 00
U-Boot> tpm2 nv_define
Input values size:34 index:0x1400001 attribute:0x44042004
Input to "tpm2_nv_define_space" values size:34 index:0x1400001 attribute:0x44042004
80 02 00 00 00 2d 00 00 01 2a 40 00 00 0c 00 00 00 09 40 00 00 09 00 00 00 00 00 00 0c 01 40 00 01 00 0b 44 04 20 04 00 00 00 00

We've found that space_size is not used anywhere on the function but it's received as an parameter to the tpm2_nv_define_space function. Hence we've modified the command_v2 buffer to have the size as follows,

u8 command_v2[COMMAND_BUFFER_SIZE] = {
    .
    .
    .
    /* nv_policy */
    tpm_u16(space_size),
};

Now we're receiving a different error (730 - 0x2da - the TPM was unable to unmarshal a value because there were not enough octets in the input buffer) the RAW command values is as follows,

80 02 00 00 00 2d 00 00 01 2a 40 00 00 0c 00 00 00 09 40 00 00 09 00 00 00 00 00 00 0c 01 40 00 01 00 0b 44 04 20 04 00 00 00 22

So to summarise our questions,

  1. What is the use of space_size? Will it define the size of the NV space nvPublic.dataSize?
  2. We've also noted that there's size difference (bytes missing) in the RAW command buffer that you've sent vs the one we captured on U-Boot. Is it acceptable or are we missing something?

CC @tkmozhi, @danie007

joholl commented 2 years ago

See below for the two issues with your command stream:

header:
  .tag: 80 02
  .size: 00 00 00 2d
  .commandCode: 00 00 01 2a

handle: 40 00 00 0c # platform

authArea:
  .size: 00 00 00 09
  .sessionHandle: 40 00 00 09 # password session
  .nonce: 00 00               # empty (size = 0)
  .attributes: 00
  .sessionAuth: 00 00         # empty (size = 0)

auth:             # MISSING!, try 00 00 for an empty authValue/password
publicInfo:
  .size: 00 0c    # WRONG, you need 00 0e (to account for space_size?), just count the bytes below
  .nvPublic:
    .nvIndex: 01 40 00 01    # arbitrary free NV index
    .nameAlg: 00 0b          # SHA256
    .attributes: 44 04 20 04
    .authPolicy: 00 00       # empty policy (size = 0)
    .dataSize: 00 22         # size of the NV space: 34 bytes (space_size)
  1. What is the use of space_size? Will it define the size of the NV space nvPublic.dataSize?

Yes. Seems to be a bug in U-Boot. The space_size (=nvPublic.dataSize) is the size of the NV index you want to allocate. For example, if you want to store the bytes 00 11 22 33 in the NV index, you need a space_size of 4.

  1. We've also noted that there's size difference (bytes missing) in the RAW command buffer that you've sent vs the one we captured on U-Boot. Is it acceptable or are we missing something?

You are missing something, see above.

joholl commented 2 years ago

If you could solve the problem, please

  1. Briefly state if we understood the problem correctly (and how you solved it)
  2. Link the bug fix on the U-Boot mailing list if you submitted your solution (would be great if you did!)
  3. Close the issue

Thank you!

saravanj24 commented 2 years ago

We tried by modified the command_v2 buffer as below in the do_tpm2_nv_define() function on lib/tpm-v2.c

u8 command_v2[COMMAND_BUFFER_SIZE] = {
        /* header 10 bytes */
        tpm_u16(TPM2_ST_SESSIONS),  /* TAG */
        tpm_u32(offset + nv_policy_size),/* Length */
        tpm_u32(TPM2_CC_NV_DEFINE_SPACE),/* Command code */

        /* handles 8 bytes */
        tpm_u32(TPM2_RH_PLATFORM),     /* Primary platform seed */

        /* session header 13 bytes */
        tpm_u32(9),                     /* Header size */
        tpm_u32(TPM2_RS_PW),       /* Password authorisation */
        tpm_u16(0),                     /* nonce_size */
        0,                                /* session_attrs */
        tpm_u16(0),                     /* auth_size */

        tpm_u16(0),     /* newly added  - authValue/password */

        /* message 14 bytes + policy */
        tpm_u16(14 + nv_policy_size),   /* size */  /* Modified - message bytes to 14*/
        tpm_u32(space_index),
        tpm_u16(TPM2_ALG_SHA256),
        tpm_u32(nv_attributes),
        tpm_u16(nv_policy_size),
        /* nv_policy */
        tpm_u16(space_size)           /* newly added - size of the NV space*/
    };

Observed the TPM response as success. TPM response [ret:0]: 80 02 00 00 00 13 00 00 00 00 00 00 00 00 00 00 01 00 00

Also we tried the nv_write command in U-boot with the below options. We received with the Error(644:value is out of range or is not correct for the context)

We prepared the do_tpm2_nv_write_value() function with reference to the \cmd\tpm-v1.c file.

U-Boot> tpm2 nv_write 0x1000000 11223344 
TPM request [size:39]: 80 02 00 00 00 27 00 00 01 37 40 00 00 0c 02 00 00 00 00 00 00 09 40 00 00 09 00 00 00 00 00 00 04 11 22 33 44 00 00
TPM response [ret:644]: 80 01 00 00 00 0a 00 00 02 84 

For nv_read we prepared the do_tpm2_nv_read_Value() function with reference to the \cmd\tpm-v1.c file. For that it gets three arguments (index data count) from the user.

index - Nv_index
data - ? 
count - counts of data written on the  nv_index.
  1. What is the second argument data specifies in do_tpm2_nv_read_Value()?
  2. What are the values have to pass from the CLI for nv_write/nv_read functionality?
saravanj24 commented 2 years ago

@joholl

Kindly provide your feedback on the previous query will be helpful us for using the NV read/write functionalities in u-boot. It would be much appreciated for your help on this query.

joholl commented 2 years ago

Sorry for the late answer.

Observed the TPM response as success.

Nice!

First of all, I just remembered this neat tool: https://apps.microsoft.com/store/detail/tpm-20-parser/9NBLGGH4VQW3?ocid=Apps_O_WOL_en-us_apps-main_education_text-link_brainpop-featured-movie

That will decode your command/response bytes as long as they are well-formed. If you plug in our command bytes, it says:

Header:
Tpm2Lib.CommandHeader
  Tag                   Sessions                  TpmSt
  CommandSize           39 (0x27)                 uint
  CommandCode           NvWrite                   TpmCc

Command Parameters:
Tpm2Lib.Tpm2NvWriteRequest
  authHandle            -                         TpmHandle
    handle              1073741836 (0x4000000c)   uint
  nvIndex               -                         TpmHandle
    handle              33554432 (0x2000000)      uint    <-- you need to put the NV index you defined here: 0x01400001
  data                  0x11223344                byte[4]
  offset                0 (0x0)                   ushort

Sessions [1]
0: 0xTpm2Lib.SessionIn
  handle                -                         TpmHandle
    handle              1073741833 (0x40000009)   uint
  nonceCaller           0x                        byte[0]
  attributes            None                      SessionAttr
  auth                  0x                        byte[0]

I think you need to change

80 02 00 00 00 27 00 00 01 37 40 00 00 0c 02 00 00 00 00 00 00 09 40 00 00 09 00 00 00 00 00 00 04 11 22 33 44 00 00

to

80 02 00 00 00 27 00 00 01 37 40 00 00 0c 01 40 00 01 00 00 00 09 40 00 00 09 00 00 00 00 00 00 04 11 22 33 44 00 00

Other than that, it looks fine to me, but I have not verified it myself. Hope it helps :)

joholl commented 2 years ago

@saravanj24 I hacked together joholl/tpmstream for answering that kind of question. Thought you might be interested.

python -m tpmstream example NV_Write
saravanj24 commented 2 years ago

Thank you

We verified this tool by sending the Raw byte values of the TPM commands and observed the expected results.

python -m tpmstream example NV_Write

When we tried run the example command in terminal window and no information are display in the screen. Is there any this we missing for executing the example command?