[Crypto] Communicating with TPM 2.0: the first step

tpm2_tools suite has a command to generate random data -- a PRNG in TPM 2.0 chip is used:

% tpm2_getrandom --hex 32
1b8114ec9fda3fbcce3b096439af86e34f0e7a077d4e4e54ae6cf6097e49eb09

Let's see what happens inside.

Run it with strace (-xx option is for hex dumping, -s sets max. string size):

% strace -xx -s 10240 tpm2_getrandom --hex 32

Here we can see how tpm2_getrandom communicates with tpm2_abrmd daemon via read/write:

write(3, "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x7b\x00\x08", 12) = 12
read(3, "\x80\x01\x00\x00\x00\x14\x00\x00\x00\x00", 10) = 10
read(3, "\x00\x08\x6b\x13\x20\x08\x71\x4e\x13\x96", 10) = 10

...

write(3, "\x80\x01\x00\x00\x00\x16\x00\x00\x01\x7a\x00\x00\x00\x06\x00\x00\x01\x00\x00\x00\x00\x7f", 22) = 22
read(3, "\x80\x01\x00\x00\x02\x0b\x00\x00\x00\x00", 10) = 10
read(3, "\x01\x00\x00\x00\x06\x00\x00\x00\x3f\x00\x00\x01\x00\x32\x2e\x30\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x00\x74\x00\x00\x01\x03\x00\x00\x01\x09\x00\x00\x01\x04\x00\x00\x07\xe0\x00\x00\x01\x05\x49\x46\x58\x00\x00\x00\x01\x06...

...

write(3, "\x80\x01\x00\x00\x00\x16\x00\x00\x01\x7a\x00\x00\x00\x06\x00\x00\x02\x14\x00\x00\x00\x40", 22) = 22
read(3, "\x80\x01\x00\x00\x00\x1b\x00\x00\x00\x00", 10) = 10
read(3, "\x00\x00\x00\x00\x06\x00\x00\x00\x01\x00\x00\x02\x14\x00\x00\x00\x00", 17) = 17

...

write(3, "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x7b\x00\x20", 12) = 12
read(3, "\x80\x01\x00\x00\x00\x2c\x00\x00\x00\x00", 10) = 10
read(3, "\x00\x20\xfb\x6b\xe8\x10\xc7\xf9\xc7\xd5\x19\x2d\x61\xfa\x8b\xa0\xae\x6d\xbc\x82\x82\xd9\xd6\xee\xca\x38\x07\x2f\x36\x43\xcf\xad\xeb\xa2", 34) = 34

Take a closer look, and note \x01\x7b, that is command number 0x17B:

write(3, "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x7b\x00\x08", 12) = 12

By grepping 0x17B in various TPM-related source code, I can quickly find that this is for TPM2_CC_GetRandom command.

There are also call of 0x17A command, which is for TPM2_CC_GetCapability.

I don't know why tpm2_getrandom calls TPM2_CC_GetRandom twice (first time it asks for 8-byte random buffer, second time for 32-byte buffer, like we asked).

But I can replay this command using my own script:

#!/usr/bin/python
import os, hexdump

#dev=os.open("/dev/tpm0", os.O_RDWR)
dev=os.open("/dev/tpmrm0", os.O_RDWR)
os.write(dev,b"\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x7b\x00\x20")
buf=os.read(dev,10)
hexdump.hexdump(buf)
buf=os.read(dev,34)
hexdump.hexdump(buf)

(We can easily deduce that the last byte (or 16-bit word?) is requested number of random bytes.)

Also, it's supposed that access rights you have are like:

% ls -la /dev/tpm*
crw-rw---- 1 tss root  10,   224 Oct  1 12:21 /dev/tpm0
crw-rw---- 1 tss tss  253, 65536 Oct  1 12:21 /dev/tpmrm0

To access the /dev/tpmrm0 device, add your current user to the 'tss' group:

sudo usermod -aG tss $USER

Run my Python script:

 % python3 rnd.py
00000000: 80 01 00 00 00 2C 00 00  00 00                    .....,....
00000000: 00 20 6E 53 CD 8E 81 77  2D AC 8F FC 8C FA 4B 23  . nS...w-.....K#
00000010: D9 A9 B2 77 85 12 99 37  98 73 A9 BC B1 8E D6 50  ...w...7.s.....P
00000020: 9F 45                                             .E

The answer is: 16-bit size of buffer (big-endian) and 32 random bytes, each time different.

The /dev/tpm0 device can be used as well.

What is inside Linux kernel device driver? It's almost nothing -- only i2c driver that sends our commands via i2c bus to TPM 2.0 chip (if it's discrete).

A TPM 2.0 chip can be connected via i2c bus to Raspberry Pi easily, as well as to USB via USB-i2c 'adapter'.

(the post first published at 20260210.)


List of my other blog posts. Subscribe to my news feed,
If you noticed a typo/bug/error or have any suggestions, do not hesitate to drop me a note: my emails. Or use my zulip for feedback. Thanks in advance!
If you enjoy my work, you can support it on patreon.
Some time ago (before 24-Mar-2025) there was Disqus JS script for comments. I dropped it --- it was so motley, distracting, animated, with too much ads. I never liked it. Also, comments din't appeared correctly (Disqus was buggy). Also, my blog is too chamberlike --- not many people write comments here. So I decided to switch to the model I once had at least in 2020 --- send me your comments by email (don't forget to include URL to this blog post) and I will copy&paste it here manually.
Let's party like it's ~1993-1996, in this ultimate, radical and uncompromisingly primitive pre-web1.0-style blog and website. This website is best viewed under lynx/links/elinks/w3m.