* TOTP (Time-based one-time password) on TPM 2.0 Previously: https://yurichev.com/blog/TOTP1/ https://yurichev.com/blog/TOTP2/ https://yurichev.com/blog/TPM1/ https://yurichev.com/raw-notes/cryptography/TPM/HMAC-SHA1-on-TPM-2.0.org ** Create hmac.ctx from TOTP key ( The code is the same as in lichess example: https://yurichev.com/blog/TOTP1/ ) #+BEGIN_CODE #!/usr/bin/env python3 import base64 s=base64.b32decode("XYYUFRCR3YP7DMHSZOLAQWBRTEODNAZL") with open("hmac.key", "wb") as f: f.write(s) #+END_CODE Run: #+BEGIN_CODE tpm2 loadexternal -C n -G hmac:sha1 -a "sign|userwithauth" -r hmac.key -c hmac.ctx rm hmac.key #+END_CODE ** Generate TOTP codes #+BEGIN_CODE #!/usr/bin/env python3 import time, os, tempfile def hotp(ctr: int, length: int) -> str: # https://stackoverflow.com/questions/3924117/how-to-use-tempfile-namedtemporaryfile-in-python tmp_in=tempfile.NamedTemporaryFile(delete=False) tmp_in.write(ctr.to_bytes(8, 'big')) tmp_in.close() tmp_out=tempfile.NamedTemporaryFile(delete=False) tmp_out.close() os.system (f"tpm2 hmac -g sha1 -c hmac.ctx -o {tmp_out.name} {tmp_in.name}") os.unlink(tmp_in.name) with open(tmp_out.name, "rb") as f: mac=f.read() os.unlink(tmp_out.name) offset = mac[-1] & 0xf truncated = bytearray(mac[offset:offset+4]) truncated[0] &= 0x7f value = int.from_bytes(truncated, 'big') % (10**length) return str(value).rjust(length, '0') ctr=int(time.time()//30) print (hotp(ctr,6)) valid=30-(int(time.time()) % 30) print (f"The code is valid for {valid} seconds") #+END_CODE Run it: #+BEGIN_CODE 165457 The code is valid for 28 seconds #+END_CODE ** Weakness But this solution has the same weakness as with Yubikey ( https://yurichev.com/blog/TOTP2/ ). An attacker, having access to your TPM, can generate some codes for future use. This is to be kept in mind. But TPM context can be protected with password.