From badb5eedeb2ffb87f2e367723bb19076590d67c2 Mon Sep 17 00:00:00 2001 From: salrashid123 Date: Thu, 30 May 2024 16:24:36 -0400 Subject: [PATCH] breaking change: use tpmdirect; remove go-tpm-tools client; add session encryption --- README.md | 342 +++++++------ example/go.mod | 36 +- example/go.sum | 82 ++- example/go_keyfile_compat/main.go | 342 +++++++++++++ example/go_tpm_tools_compat/main.go | 196 ++++++++ example/nopolicy/main.go | 137 +++-- example/policy/main.go | 95 ---- example/policy_password/main.go | 187 +++++++ example/policy_pcr/main.go | 199 ++++++++ example/session_encryption/main.go | 201 ++++++++ example/util/keycreate.go | 209 -------- go.mod | 8 +- go.sum | 13 +- tpmsigner.go | 234 +++++++-- tpmsigner_test.go | 748 +++++++++++++++++++++++----- 15 files changed, 2283 insertions(+), 746 deletions(-) create mode 100644 example/go_keyfile_compat/main.go create mode 100644 example/go_tpm_tools_compat/main.go delete mode 100644 example/policy/main.go create mode 100644 example/policy_password/main.go create mode 100644 example/policy_pcr/main.go create mode 100644 example/session_encryption/main.go delete mode 100644 example/util/keycreate.go diff --git a/README.md b/README.md index bf1c555..e02651d 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,11 @@ This is just an extension for [go-jwt](https://github.com/golang-jwt/jwt#extensi You can use this library to sign and verify a JWT using the standard `go-jwt` library semantics. -This library also includes a utility function to create an RSA key inside a TPM and also print its public key in `RSA` and `JWK` formats. - Using a TPM to sign or encrypt anything has some very specific applications which i will not go into it much (if your'e reading this, you probably already know). If a JWT is signed by a TPM and if the key that was used was setup in a specific format, the verifier can be sure that the JWT was signed by that TPM. For example, you can use a TPM to generate an RSA key with specifications that "this key was generated on a TPM with characteristics such that it cannot get exportable outside the TPM"..very necessarily, the RSA private key will never exist anywhere else other than in that TPM. -How a you trust that a specific RSA or ECC key happens to be from a given TPM with a given specification set is a rather complicated protocol that is also not covered in this repo. The specific trust protocol is called [TPM Remote Attestation](https://tpm2-software.github.io/tpm2-tss/getting-started/2019/12/18/Remote-Attestation.html). +How a you trust that a specific `RSA` or `ECC` key happens to be from a given TPM with a given specification set is a rather complicated protocol that is also not covered in this repo. The specific trust protocol is called [TPM Remote Attestation](https://tpm2-software.github.io/tpm2-tss/getting-started/2019/12/18/Remote-Attestation.html). This repo assumes the verifier of the JWT has already established that the RSA key that is being used to sign the JWT @@ -28,31 +26,29 @@ against the TPM `OWNER` hierarchy ### Usage -Use this library to issue JWTs in a way compatible with golang-jwt library. The difference is that the caller must initialize a `client.Key` object from [go-tpm-tools](https://github.com/google/go-tpm-tools): +Use this library to issue JWTs in a way compatible with golang-jwt library. The difference is that the caller must initialize a low-level [go-tpm/tpm2.TPMHandle][https://pkg.go.dev/github.com/google/go-tpm@v0.9.0/tpm2#TPMHandle] object from [go-tpm](https://github.com/google/go-tpm) and pass that through with any optional authorized session: ```golang import ( "github.com/golang-jwt/jwt/v5" tpmjwt "github.com/salrashid123/golang-jwt-tpm" - "github.com/google/go-tpm-tools/client" - "github.com/google/go-tpm/legacy/tpm2" + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpm2/transport" "github.com/google/go-tpm/tpmutil" ) // initialize the TPM rwc, err := tpm2.OpenTPM(*tpmPath) defer rwc.Close() +rwr := transport.FromReadWriter(rwc) -// get a client.Key object which references the embedded RSA key -var k *client.Key -k, err = client.LoadCachedKey(rwc, tpmutil.Handle(*persistentHandle), nil) -//k, err = client.LoadCachedKey(rwc, tpmutil.Handle(*persistentHandle), client.EKSession{}) - -// pass those to this library +// get an existing tpm based keys persistent or transient handle +// pass that to this library along with any session authorization config := &tpmjwt.TPMConfig{ TPMDevice: rwc, - Key: k, + Handle: tpm2.TPMHandle(keyHandle), + Session: tpm2.PasswordAuth(nil), } keyctx, err := tpmjwt.NewTPMContext(ctx, config) @@ -65,6 +61,7 @@ claims := &jwt.RegisteredClaims{ tpmjwt.SigningMethodTPMRS256.Override() token := jwt.NewWithClaims(tpmjwt.SigningMethodTPMRS256, claims) tokenString, err := token.SignedString(keyctx) + fmt.Printf("TOKEN: %s\n", tokenString) ``` @@ -115,141 +112,128 @@ rm -rf /usr/local/go && tar -C /usr/local -xzf go1.22.2.linux-amd64.tar.gz export PATH=$PATH:/usr/local/go/bin ``` -Once on the VM, create a key on TPM (if you already have an existing key on TPM, you can acquire a handle using `go-tpm-tools`). For now, create a key - -The key created is _persisted_ at a handle (default `0x81008001`) and you can pick any defined in pg 15 of [Registry of Reserved TPM 2.0 Handles and Localities](https://trustedcomputinggroup.org/wp-content/uploads/RegistryOfReservedTPM2HandlesAndLocalities_v1p1_pub.pdf) - +Once on the VM, create a key on TPM (if you already have an existing key on TPM, you can acquire a handle and pass that to the library). ### Usage - -For simplicity, the following generates and embeds keys into a persistent handle using [tpm2_tools](https://github.com/tpm2-software/tpm2-tools) +For simplicity, the following generates and embeds keys into a persistent handle using [tpm2_tools](https://github.com/tpm2-software/tpm2-tools). (You are free to use any system to provision a key) #### RSA -Create RSA key at handle `0x81008001`, RSA-PSS handle at `0x81008005` +Create RSA key at handle `0x81008001`, RSA-PSS handle at `0x81008004`. ```bash -# or with tpm2_tools -# tpm2_flushcontext -t -s -l -# tpm2_evictcontrol -C o -c 0x81008001 -## for rsa - tpm2_createprimary -C o -c primary.ctx - tpm2_create -G rsa2048:rsassa:null -g sha256 -u key.pub -r key.priv -C primary.ctx --format=pem --output=rsa_public.pem - tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx - tpm2_evictcontrol -C o -c key.ctx 0x81008001 +## RSA - no password + tpm2_createprimary -C o -G rsa2048:aes128cfb -g sha256 -c primary.ctx -a 'restricted|decrypt|fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda' + tpm2_create -G rsa2048:rsassa:null -g sha256 -u key.pub -r key.priv -C primary.ctx + tpm2_flushcontext -t + tpm2_getcap handles-transient + tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx + tpm2_evictcontrol -C o -c key.ctx 0x81008001 + tpm2_flushcontext -t ## rsa-pss - tpm2_createprimary -C o -c primary.ctx - tpm2_create -G rsa2048:rsapss:null -g sha256 -u key.pub -r key.priv -C primary.ctx --format=pem --output=rsapss_public.pem - tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx - tpm2_evictcontrol -C o -c key.ctx 0x81008005 + tpm2_createprimary -C o -G rsa2048:aes128cfb -g sha256 -c primary.ctx -a 'restricted|decrypt|fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda' + tpm2_create -G rsa2048:rsapss:null -g sha256 -u key.pub -r key.priv -C primary.ctx --format=pem --output=rsapss_public.pem + tpm2_flushcontext -t + tpm2_getcap handles-transient + tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx + tpm2_evictcontrol -C o -c key.ctx 0x81008004 + tpm2_flushcontext -t + +## ecc + tpm2_createprimary -C o -G rsa2048:aes128cfb -g sha256 -c primary.ctx -a 'restricted|decrypt|fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda' + tpm2_create -G ecc:ecdsa -g sha256 -u key.pub -r key.priv -C primary.ctx --format=pem --output=ecc_public.pem + tpm2_flushcontext -t + tpm2_getcap handles-transient + tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx + tpm2_evictcontrol -C o -c key.ctx 0x81008005 + tpm2_flushcontext -t ``` -Then, +Then run, ```bash cd example/ ## RS256 -$ go run nopolicy/main.go --mode=rsa --persistentHandle=0x81008001 - - 2024/04/21 12:48:04 ======= Init ======== - 2024/04/21 12:48:04 Signing PEM - -----BEGIN PUBLIC KEY----- - MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwUgQGTtRGfy/aI3rwXmA - LSYWzoGdLGNimDP0r8fLwh2pti01bJIBq+O3i0LlZRoQWNWvVdb2ZpcUfdJFiPRl - cZgXLwnJUtPC+1vnMt3KEkjknt3f/WD5eCXL903Wg4BfHISL9myQTcAXB9KC30bb - PaELzw3NTR8N999vdU9ny1YL19Ua9gbJlti2jv+8V6CBUxcvlN2YvdvwrRZyyb2n - wODKiiUguOJoYbH2+urqiWzuNKi/H8Cm8+cYZBCzdVlb+BT6y9CWRdwh6kJGkSla - 7EDVMyVysB/urg3ypXvHbDvBMfNTPhfsdZmDfF58LUs7lM7Rr4d/hi2udqFS8ipp - nwIDAQAB - -----END PUBLIC KEY----- - TOKEN: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0IiwiZXhwIjoxNzEzNzAzNzQ0fQ.FciBN-71_83nlOTb3ucIf4L2ULUyANqn8H_kLST4LJ7O2keSrSUyOQxZGu32r-9wNJyGbHbE0inDLTEz3L3hp0gkn10WsAMofNQVl4Pefm0lFkkGgJ4RYJyjTi--VA1K52nqjVQwJa_mwGpvVVas7iMZ0IpXvlDOoeWUfCS1E-udKmZjHu-rcgZ8k0Jt16GmlEtCd9Qw4hlJWNSdVPyWtbjYvUV8JNm95raE6Y-7e_EsRF82miUcsj1yTwF22IfAg_RfGe_NsIQqAHU8yczJi9QcTHQna5gmwOWIPNBdABQpTUP2vAUq7cT2XfTlHE_hlONLl66XsUL-tehg4ykubg - 2024/04/21 12:48:04 verified with TPM PublicKey - 2024/04/21 12:48:04 verified with exported PubicKey - +$ go run nopolicy/main.go --mode=rsa --persistentHandle=0x81008001 --tpm-path=/dev/tpm0 + + 2024/05/30 11:26:54 ======= Init ======== + 2024/05/30 11:26:54 primaryKey Name AAvaZWBJngiVUFq6Dg/Q7uBxAK3INE3G/GOsnm7v0TGujQ== + 2024/05/30 11:26:54 Signing PEM + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYHlZpdRyRvxSdcM18as + /j6UruSGKOYgUjpt05h8z0NienvEKlSt0YJxPm2hIQBQAvZ5oR5aLNVUMePd6XHF + wjNAkaME9KJB9KAQPMrEv7+WWAuBq8ImPCziEeXLBnWR4Bj6CsqXFNNq/q/FfJZv + /iLD9IKMqNz/ChPHDJ4ZNRZRUCyHUG6+IgYIovbqT/YzE0nhAlU2EU1tj2+SBOBV + p2VeqMMXIMfJVXRWAFbi3nR8TtQ04TBbGGNaG/+WvKnruT5CuiQ+V8wvHGnV84ux + TiVIQV2nt57dRTodbEuzpyxES3gs2sOqC6KRZNVJXnz2IugqdkItHjnwR2KvnUEn + NwIDAQAB + -----END PUBLIC KEY----- + TOKEN: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0IiwiZXhwIjoxNzE3MDgyODc0fQ.ETa4wNzr-NkUN9VLyCS0_8ZJkSsHxd_xbLfHnRr-wtjdD8-XqlYJ7ehYZRC677u_tda3AHftS8uXlPN1jNnbw5sAq7E50IyS2LICExc6SHPuGrsh_O4GN1sM4Gbaxk-KjRYIlePFbiepc0liyEglan4gjEySBZMrIzItvKBEfq-sC092RysfARggnRgUxNf49zlYPX8jTYL2OW46cc2c4qOurnDQhWWSn4MqfcfMh932eMBqW_i1obIcD_LjlQxfmJ7-e1Dm2n86CyFEHWe0ANQ3ixEp8ybuLzbU_KB3wFtnXJMn_iifoKJPpzFMds5d5GdeW_jiikiB1Eb7PUChlg + 2024/05/30 11:26:54 verified with TPM PublicKey + 2024/05/30 11:26:54 verified with exported PubicKey -## PS256 -$ go run nopolicy/main.go --mode=rsapss --persistentHandle=0x81008005 - - 2024/04/21 12:48:22 ======= Init ======== - 2024/04/21 12:48:22 Signing PEM - -----BEGIN PUBLIC KEY----- - MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnn5LqHf/ZKydt4jXMjoD - f1JFQ0pyyoqIiQLvmvJPHWdJmXO3MK0QZGdoaN+vqx3LWX5zcmilbwtrW5uqvNwN - IE4Du1moBfeyHJFbICFG/0r1Wx2dPJDpTroqO59QWMzGtyQuCrxNNTnvmt77mhyJ - Wu5u4LR7r8PvZpKHKAoGt5ey4238dzZIZU3+it3UcxWJ30d2YklIVxjBDmlgu/NC - YSB6mNd7VBN+ha+k5P0KAI2HfBlvd1t8ptQJvTz5QjMzZJ5yg1XEmNDF2kx1Px4A - NigN/lR0txgjqwmG+MtQPtp4YMfIp5ZwOWdorUZ8GzIlXktW4qsE08EH6n+ha2rW - WwIDAQAB - -----END PUBLIC KEY----- - TOKEN: eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0IiwiZXhwIjoxNzEzNzAzNzYyfQ.d-2PSl4q7j9QzM8iYN0ggUShnq3ODeKq1fSrqMpMONokK4lH6cEHYl0xFYwkrlsPZxW3z0YKroNZ5Hq-eLrReiMJniPk2sCieX30TrsKcbuFEYlmeZauY0YwAa3BxvlAs9yCe9fx_7GUVqONWK12O22mEToPHG-syp2J89WZxUVdny-bKrWs-9PYbdnHBYx-XokSSIjJj3nivo20mzDtampG1fBNDp7-ZWxyuotS7qH_r5_WfOfyfR_FtsBKF1omBApZA4vQC8n1kRCZ3wUi-PhbgDyvipz3JcxW1J5SvSLZv9UCTwLdtFl6SzBaBFEsuOZ_N3Oy6rK95d5wayjUrw - 2024/04/21 12:48:22 verified with TPM PublicKey - 2024/04/21 12:48:22 verified with exported PubicKey - -``` -Note the public keys for the tpm's handle ofcourse match the respective outputs above -```bash -# print the public keys -$ cat rsa_public.pem ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwUgQGTtRGfy/aI3rwXmA -LSYWzoGdLGNimDP0r8fLwh2pti01bJIBq+O3i0LlZRoQWNWvVdb2ZpcUfdJFiPRl -cZgXLwnJUtPC+1vnMt3KEkjknt3f/WD5eCXL903Wg4BfHISL9myQTcAXB9KC30bb -PaELzw3NTR8N999vdU9ny1YL19Ua9gbJlti2jv+8V6CBUxcvlN2YvdvwrRZyyb2n -wODKiiUguOJoYbH2+urqiWzuNKi/H8Cm8+cYZBCzdVlb+BT6y9CWRdwh6kJGkSla -7EDVMyVysB/urg3ypXvHbDvBMfNTPhfsdZmDfF58LUs7lM7Rr4d/hi2udqFS8ipp -nwIDAQAB ------END PUBLIC KEY----- - -$ cat rsapss_public.pem ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnn5LqHf/ZKydt4jXMjoD -f1JFQ0pyyoqIiQLvmvJPHWdJmXO3MK0QZGdoaN+vqx3LWX5zcmilbwtrW5uqvNwN -IE4Du1moBfeyHJFbICFG/0r1Wx2dPJDpTroqO59QWMzGtyQuCrxNNTnvmt77mhyJ -Wu5u4LR7r8PvZpKHKAoGt5ey4238dzZIZU3+it3UcxWJ30d2YklIVxjBDmlgu/NC -YSB6mNd7VBN+ha+k5P0KAI2HfBlvd1t8ptQJvTz5QjMzZJ5yg1XEmNDF2kx1Px4A -NigN/lR0txgjqwmG+MtQPtp4YMfIp5ZwOWdorUZ8GzIlXktW4qsE08EH6n+ha2rW -WwIDAQAB ------END PUBLIC KEY----- -``` - - -#### ECC - -```bash - tpm2_createprimary -C e -c primary.ctx - tpm2_create -G ecc:ecdsa -g sha256 -u key.pub -r key.priv -C primary.ctx --format=pem --output=ecc_public.pem - tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx - tpm2_evictcontrol -C o -c key.ctx 0x81008002 +## PS256 +$ go run nopolicy/main.go --mode=rsapss --persistentHandle=0x81008004 --tpm-path=/dev/tpm0 + + 2024/05/30 11:27:10 ======= Init ======== + 2024/05/30 11:27:10 primaryKey Name AAvaZWBJngiVUFq6Dg/Q7uBxAK3INE3G/GOsnm7v0TGujQ== + 2024/05/30 11:27:10 Signing PEM + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2oTpzfYtQvejOHPmlRUX + /6jWaH31sbM50XfCPfQ0q622mcKr2Cg4imnw0NxtCz2sOHNef/xEUiSHL3HFrK0T + 49Iy0INLo4yl07iURx+/4uriKuLEgfEkDrLMQthDD/a853Q5CIjbcqmxEIm4oS0J + mdSJwDNpksUwPRu96wGeD/NLVpF+uK/yVRAkAJnIu16cSKivN/f02CcHDaTg/qZg + bRf9B/sBXQRrv4R+cZkgjK/dXMIpQz7SABgvIOWnvOwjQHFXdH7vrZLpxlPaL3T+ + QcJvm/Xk7nrzJCBsyGBPlMjtGv1W1933M5w96rBZqPTJAGKLHcE5BVdZSge1ZTW4 + kwIDAQAB + -----END PUBLIC KEY----- + TOKEN: eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0IiwiZXhwIjoxNzE3MDgyODkwfQ.PTWmvgLXE4oVVdNW_fZr5_BZbcYAghUfdyaFIYCmQHXqJ60alHeZX3w9Vr3p62bWtX7LrIKMrOMqKfhcUz92fBYcx2z1BY1Y3RS6VyP3FUgckH4puFA8kU6Z7bFalgqVGV03B3jnlpRyNZOhbtcEHgf4XplmP_5ZIykw8q6ekChwyYrCwu03-m10lH_R6q4YKC_LV4sjcsvV4ZCTnZWo07ggbv8NUWECr13wu7ChWaD8UrvsUdhXXGMGnS_xtqKKvQjSL5EqSjmp8_PO10CI2x0ZgKFYY4aqh_CFQr-lT5qzkIgv9R5GzLPCdaa8NBpWx2YaTore61miXXLxdiJFwg + 2024/05/30 11:27:10 verified with TPM PublicKey + 2024/05/30 11:27:10 verified with exported PubicKey + + +## ES356 +$ go run nopolicy/main.go --mode=ecc --persistentHandle=0x81008005 --tpm-path=/dev/tpm0 + + 2024/05/30 11:27:35 ======= Init ======== + 2024/05/30 11:27:35 primaryKey Name AAvaZWBJngiVUFq6Dg/Q7uBxAK3INE3G/GOsnm7v0TGujQ== + 2024/05/30 11:27:35 Signing PEM + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5UHzO3QR1iS+D4+5F8fwiYxigTlO + eL0hcqOz4DDbhQtxBuYjnVD7tCgVLN0riqCSgjh150j9E9xSDi0E55dFug== + -----END PUBLIC KEY----- + TOKEN: eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0IiwiZXhwIjoxNzE3MDgyOTE1fQ.rm2RNGLnmKbLkdZbrkBxyd674VPX-VtKODNLDQgea_W1IRSMtKIaFWDzkuap3NGTVqsF-A9sIkAGRCdqAqF4rQ + 2024/05/30 11:27:35 verified with TPM PublicKey + 2024/05/30 11:27:35 verified with exported PubicKey ``` -Then +### Session Encryption -```bash -cd example/ +If you want to enable [session encryption](https://github.com/salrashid123/tpm2/tree/master/tpm_encrypted_session), you need to supply an external key you know to be associated with a TPM (eg an Endorsement Key): -$ go run nopolicy/main.go --mode=ecc --persistentHandle=0x81008002 +```golang + createEKCmd := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHEndorsement, + InPublic: tpm2.New2B(tpm2.RSAEKTemplate), + } + createEKRsp, err := createEKCmd.Execute(rwr) - 2024/04/21 12:49:37 ======= Init ======== - 2024/04/21 12:49:37 Signing PEM - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAd6Ei4Xwv/euPIaWw5LXHbbXAQ6B - Syvq/jt3yTPeLybanA2CnIOEIns3pFMGbikuy/0FEa/0iAeQdDVpxGwRXg== - -----END PUBLIC KEY----- - TOKEN: eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0IiwiZXhwIjoxNzEzNzAzODM3fQ.K5qHHoR4sbzPcgxVPbYmXZAyU0u5PRSJ00Aup-A_ddIflXIThIXTlynAXiMg0AGaOEx1JFEkLpBkWbHSsG4q8Q - 2024/04/21 12:49:37 verified with TPM PublicKey - 2024/04/21 12:49:37 verified with exported PubicKey + encryptionPub, err := createEKRsp.OutPublic.Contents() + config := &tpmjwt.TPMConfig{ + TPMDevice: rwc, + Handle: tpm2.TPMHandle(*persistentHandle), + EncryptionHandle: createEKRsp.ObjectHandle, + EncryptionPub: encryptionPub, + } ``` -Notice the public key matches the one we saved to the handle - - -You must have a key already defined and persisted to NV (transient keys not supported) +Once you do that, the bus traffic is also encrypted ### Imported Key @@ -258,17 +242,13 @@ If you want to [import an external RSA key to the TPM](https://github.com/salras using `tpm2_tools`: ```bash -openssl genrsa -out private.pem 2048 -openssl rsa -in private.pem -outform PEM -pubout -out public.pem - -tpm2_createprimary -C o -g sha256 -G rsa -c primary.ctx -tpm2_import -C primary.ctx -G rsa2048:rsassa:null -g sha256 -i private.pem -u key.pub -r key.prv -tpm2_load -C primary.ctx -u key.pub -r key.prv -c key.ctx -tpm2_evictcontrol -C o -c key.ctx 0x81008003 + openssl genrsa -out private.pem 2048 + openssl rsa -in private.pem -outform PEM -pubout -out public.pem -echo "my message" > message.dat -tpm2_sign -c key.ctx -g sha256 -o sig1.rssa message.dat -tpm2_verifysignature -c key.ctx -g sha256 -s sig1.rssa -m message.dat + tpm2_createprimary -C o -g sha256 -G rsa -c primary.ctx + tpm2_import -C primary.ctx -G rsa2048:rsassa:null -g sha256 -i private.pem -u key.pub -r key.prv + tpm2_load -C primary.ctx -u key.pub -r key.prv -c key.ctx + tpm2_evictcontrol -C o -c key.ctx 0x81008006 ``` You can also see how to load the entire chain here [Loading TPM key chains](https://github.com/salrashid123/tpm2/context_chain) @@ -276,68 +256,67 @@ You can also see how to load the entire chain here [Loading TPM key chains](http #### With Session and Policy -If a key is bound to a PCR policy, you can specify that inline during key initialization. +If a key is bound to a Password or PCR policy, you can specify that inline during key initialization. -For example, the following has `PCR=23` policy bound: +For example, the following has password policy bound: ```golang - s, err := client.NewPCRSession(rwc, tpm2.PCRSelection{tpm2.AlgSHA256, []int{23}}) - defer s.Close() - - sessionKey, err := client.LoadCachedKey(rwc, tpmutil.Handle(*persistentHandle), s) - defer sessionKey.Close() - - sessionConfig := &tpmjwt.TPMConfig{ + keyPass := []byte("pass2") + config := &tpmjwt.TPMConfig{ TPMDevice: rwc, - Key: sessionKey, + Handle: tpm2.TPMHandle(*persistentHandle), + Session: tpm2.PasswordAuth(keyPass), } + keyctx, err := tpmjwt.NewTPMContext(ctx, config) ``` Which you can initialize though: ```bash -## first print the value at pcr 23: -# tpm2_flushcontext -t -s -l -# tpm2_evictcontrol -C o -c 0x81008004 -# tpm2_pcrread sha256:23 -# sha256: -# 23: 0x0000000000000000000000000000000000000000000000000000000000000000 - -## you can optionally 'extend' the value to get a new PCR to use (default for 23 if unset is usually all those 0's) -# tpm2_pcrextend 23:sha256=0x0000000000000000000000000000000000000000000000000000000000000000 -# tpm2_pcrread sha256:23 -# sha256: -# 23: 0xF5A5FD42D16A20302798EF6ED309979B43003D2320D9F0E8EA9831A92759FB4B - -## create an auth session and the two policies -tpm2_startauthsession -S session.dat -tpm2_policypcr -S session.dat -l sha256:23 -L policy.dat -tpm2_flushcontext session.dat - -## create the parent -tpm2_createprimary -C o -c primary2.ctx - -tpm2_create -G rsa2048:rsassa:null -g sha256 -u rsa2.pub -r rsa2.priv -C primary2.ctx -L policy.dat -tpm2_load -C primary2.ctx -u rsa2.pub -r rsa2.priv -c rsa2.ctx - -## finally make the key persistent -tpm2_evictcontrol -C o -c rsa2.ctx 0x81008004 +## RSA - password + + tpm2_createprimary -C o -G rsa2048:aes128cfb -g sha256 -p pass1 -c primary.ctx -a 'restricted|decrypt|fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda' + tpm2_create -G rsa2048:rsassa:null -g sha256 -P pass1 -p pass2 -u key.pub -r key.priv -C primary.ctx + tpm2_flushcontext -t + tpm2_getcap handles-transient + tpm2_load -C primary.ctx -P pass1 -u key.pub -r key.priv -c key.ctx + tpm2_evictcontrol -C o -c key.ctx 0x81008002 + tpm2_flushcontext -t + +## RSA - pcr + + tpm2_pcrread sha256:23 + tpm2_startauthsession -S session.dat + tpm2_policypcr -S session.dat -l sha256:23 -L policy.dat + tpm2_flushcontext session.dat + tpm2_flushcontext -t + tpm2_createprimary -C o -G rsa2048:aes128cfb -g sha256 -c primary.ctx -a 'restricted|decrypt|fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda' + tpm2_create -G rsa2048:rsassa:null -g sha256 -u key.pub -r key.priv -C primary.ctx -L policy.dat + tpm2_flushcontext -t + tpm2_getcap handles-transient + tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx + tpm2_evictcontrol -C o -c key.ctx 0x81008003 + tpm2_flushcontext -t + ``` Then, ```bash cd example/ -go run policy/main.go --persistentHandle=0x81008004 -``` +## passwordAuth +$ go run policy_password/main.go --persistentHandle=0x81008002 --tpm-path=/dev/tpm0 -For more information, see [TPM2 Policy](https://github.com/salrashid123/tpm2/tree/master/policy) +## pcrAuth +$ go run policy_pcr/main.go --persistentHandle=0x81008003 --tpm-path=/dev/tpm0 +``` +For more information, see [TPM2 Policy](https://github.com/salrashid123/tpm2/tree/master/policy) ### Sign/Verify with GCP builtin AKCert -vTPMs usially have an EK certificate and template encoded into NV area here described from pg 13 of [TCG EK Credential Profile](https://trustedcomputinggroup.org/wp-content/uploads/TCG_IWG_EKCredentialProfile_v2p4_r3.pdf) +vTPMs usually have an EK certificate and template encoded into NV area here described from pg 13 of [TCG EK Credential Profile](https://trustedcomputinggroup.org/wp-content/uploads/TCG_IWG_EKCredentialProfile_v2p4_r3.pdf) ``` 2.2.1.4 Low Range @@ -350,7 +329,8 @@ The Low Range is at NV Indices 0x01c00002 - 0x01c0000c. 0x01c0000c ECC NIST P256 EK Template ``` -To see this and read cthe EKCertificate, +To see this and read the EKCertificate, + ```bash export TPM2_EK_NV_INDEX=0x1c00002 tpm2_nvreadpublic | sed -n -e "/""$TPM2_EK_NV_INDEX""/,\$p" | sed -e '/^[ \r\n\t]*$/,$d' | grep "size" | sed 's/.*size.*://' | sed -e 's/^[[:space:]]*//' | sed -e 's/[[:space:]]$//' @@ -358,7 +338,7 @@ tpm2_nvreadpublic | sed -n -e "/""$TPM2_EK_NV_INDEX""/,\$p" | sed -e '/^[ \r\n\t tpm2_nvread -s 1422 -C o $TPM2_EK_NV_INDEX | openssl x509 --inform DER -text -noout -in - ``` -However, GCE VMs encodes a default the Attestation Certificate into NV area here: +Note GCE VMs encodes a default the Attestation Certificate into NV area here: ```golang // RSA 2048 AK. @@ -371,7 +351,6 @@ which you can read in directly: ```bash $ tpm2_nvreadpublic - 0x1c10000: name: 000bc1dcc77bde4982d4817bcbe8418d49c1f24e3a017e79e1be9d25f6bc50c0f7c2 hash algorithm: @@ -388,7 +367,7 @@ export GceAKCertNVIndexRSA=0x01c10000 tpm2_nvread -s 1516 -C o $GceAKCertNVIndexRSA | openssl x509 --inform DER -text -noout -in - ``` -To sign with the attestation key (which is available remotely via GCE APIs and even signed by GCE), just specify +To sign with the attestation key (which is available remotely via GCE APIs and even signed by GCE), just a handle...eg using go-tpm-tools client: ```golang // attestation key that is signed by GCE @@ -396,6 +375,21 @@ sessionKey, err := client.GceAttestationKeyRSA(rwc) //sessionKey, err := client.AttestationKeyRSA(rwc) ``` +### Usign Simulator + +If you down't want to run the tests on a real TPM, you can opt to use `swtpm` if its installed: + +```bash +mkdir /tmp/myvtpm +sudo swtpm socket --tpmstate dir=/tmp/myvtpm --tpm2 --server type=tcp,port=2321 --ctrl type=tcp,port=2322 --flags not-need-init,startup-clear + +## run any TPM command +export TPM2TOOLS_TCTI="swtpm:port=2321" +tpm2_pcrread sha256:23 +``` + +--- + also see - [Sign, Verify and decode using Google Cloud vTPM Endorsement and Attestation Key and Certificate](https://github.com/salrashid123/gcp-vtpm-ek-ak) diff --git a/example/go.mod b/example/go.mod index 4e7e33f..20a49bf 100644 --- a/example/go.mod +++ b/example/go.mod @@ -1,35 +1,33 @@ module main -go 1.20 +go 1.22.0 + +toolchain go1.22.2 require ( - github.com/google/go-tpm-tools v0.4.0 - github.com/salrashid123/golang-jwt-tpm v0.0.0 + github.com/google/go-tpm v0.9.1-0.20240514145214-58e3e47cd434 + github.com/google/go-tpm-tools v0.4.4 ) require ( + github.com/foxboron/go-tpm-keyfiles v0.0.0-20240525122353-0883da4eb332 github.com/golang-jwt/jwt/v5 v5.2.1 - github.com/google/go-tpm v0.9.0 - github.com/lestrrat-go/jwx/v2 v2.0.21 + github.com/salrashid123/golang-jwt-tpm v1.5.0 ) require ( - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/google/go-sev-guest v0.6.1 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/go-configfs-tsm v0.2.2 // indirect + github.com/google/go-sev-guest v0.11.1 // indirect + github.com/google/go-tdx-guest v0.3.1 // indirect github.com/google/logger v1.1.1 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/lestrrat-go/blackmagic v1.0.2 // indirect - github.com/lestrrat-go/httpcc v1.0.1 // indirect - github.com/lestrrat-go/httprc v1.0.5 // indirect - github.com/lestrrat-go/iter v1.0.2 // indirect - github.com/lestrrat-go/option v1.0.1 // indirect - github.com/pborman/uuid v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/pborman/uuid v1.2.1 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/segmentio/asm v1.2.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/sys v0.18.0 // indirect - google.golang.org/protobuf v1.28.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/sys v0.20.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect ) replace github.com/salrashid123/golang-jwt-tpm => ../ diff --git a/example/go.sum b/example/go.sum index 5f69d0d..e994376 100644 --- a/example/go.sum +++ b/example/go.sum @@ -1,64 +1,50 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/foxboron/go-tpm-keyfiles v0.0.0-20240525122353-0883da4eb332 h1:Cg18duIK8XCYDTJWqFEQrUbYgGBeswBGyW4M23hdhQE= +github.com/foxboron/go-tpm-keyfiles v0.0.0-20240525122353-0883da4eb332/go.mod h1:Y5SsZTulz5NFq7aigID+rsWMgAq72YHHTUD0Zo2iar8= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/certificate-transparency-go v1.1.2 h1:4hE0GEId6NAW28dFpC+LrRGwQX5dtmXQGDbg8+/MZOM= -github.com/google/go-attestation v0.4.4-0.20230613144338-a9b6eb1eb888 h1:HURgKPRPJSozDuMHpjdV+iyFVLhB6bi1JanhGgSzI1k= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/certificate-transparency-go v1.1.2/go.mod h1:3OL+HKDqHPUfdKrHVQxO6T8nDLO0HF7LRTlkIWXaWvQ= +github.com/google/go-attestation v0.5.0 h1:jXtAWT2sw2Yu8mYU0BC7FDidR+ngxFPSE+pl6IUu3/0= +github.com/google/go-attestation v0.5.0/go.mod h1:0Tik9y3rzV649Jcr7evbljQHQAsIlJucyqQjYDBqktU= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-sev-guest v0.6.1 h1:NajHkAaLqN9/aW7bCFSUplUMtDgk2+HcN7jC2btFtk0= -github.com/google/go-sev-guest v0.6.1/go.mod h1:UEi9uwoPbLdKGl1QHaq1G8pfCbQ4QP0swWX4J0k6r+Q= -github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= -github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= -github.com/google/go-tpm-tools v0.4.0 h1:bYRZAUvQEmn11WTKCkTLRCCv4aTlOBgBBeqCK0ABT2A= -github.com/google/go-tpm-tools v0.4.0/go.mod h1:G7PFUk8KKQzdYYGv/cpV9LB9sPT7czAAomnceugzNKQ= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-configfs-tsm v0.2.2 h1:YnJ9rXIOj5BYD7/0DNnzs8AOp7UcvjfTvt215EWcs98= +github.com/google/go-configfs-tsm v0.2.2/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo= +github.com/google/go-sev-guest v0.11.1 h1:gnww4U8fHV5DCPz4gykr1s8SEX1fFNcxCBy+vvXN24k= +github.com/google/go-sev-guest v0.11.1/go.mod h1:qBOfb+JmgsUI3aUyzQoGC13Kpp9zwLeWvuyXmA9q77w= +github.com/google/go-tdx-guest v0.3.1 h1:gl0KvjdsD4RrJzyLefDOvFOUH3NAJri/3qvaL5m83Iw= +github.com/google/go-tdx-guest v0.3.1/go.mod h1:/rc3d7rnPykOPuY8U9saMyEps0PZDThLk/RygXm04nE= +github.com/google/go-tpm v0.9.1-0.20240514145214-58e3e47cd434 h1:uPadaCeI0VnloLvthGLalr0Io0IDoI1VEQ95APzVAiw= +github.com/google/go-tpm v0.9.1-0.20240514145214-58e3e47cd434/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= +github.com/google/go-tpm-tools v0.4.4 h1:oiQfAIkc6xTy9Fl5NKTeTJkBTlXdHsxAofmQyxBKY98= +github.com/google/go-tpm-tools v0.4.4/go.mod h1:T8jXkp2s+eltnCDIsXR84/MTcVU9Ja7bh3Mit0pa4AY= github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus= +github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI= github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ= github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= -github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= -github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= -github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk= -github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= -github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= -github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= -github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55FHrR0= -github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM= -github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= -github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= -github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= +github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= -github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/example/go_keyfile_compat/main.go b/example/go_keyfile_compat/main.go new file mode 100644 index 0000000..abaed2a --- /dev/null +++ b/example/go_keyfile_compat/main.go @@ -0,0 +1,342 @@ +package main + +import ( + "bytes" + "context" + "crypto" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "flag" + "fmt" + "io" + "log" + "net" + "os" + "slices" + "time" + + keyfile "github.com/foxboron/go-tpm-keyfiles" + jwt "github.com/golang-jwt/jwt/v5" + "github.com/google/go-tpm-tools/simulator" + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpm2/transport" + "github.com/google/go-tpm/tpmutil" + tpmjwt "github.com/salrashid123/golang-jwt-tpm" +) + +/* +Load a key using https://github.com/Foxboron/go-tpm-keyfiles + +$ go run go_keyfile_compat/main.go + +2024/05/30 11:20:36 ======= Init ======== +2024/05/30 11:20:36 ======= createPrimary ======== +2024/05/30 11:20:36 ======= create key ======== +2024/05/30 11:20:36 ======= writing key to file ======== +2024/05/30 11:20:36 rsa Key PEM: +-----BEGIN TSS2 PRIVATE KEY----- +MIICEAYGZ4EFCgEDAgUAgAAAAASCARoBGAABAAsABAByAAAAEAAUAAsIAAAAAAAB +ANXwPYXZ6NmYih5TP8JOG+d9iLQ+Bq87Ev8tTGwUBSeMARYbjZ2Kxhd6QlgPisp7 +rGrYEiSgFC3r4E9D27BnXkg+KOw6tn1l2m5+JPY7FqKYbqnaTTdWjDsnmHXBZGMZ +bo5eA7lSgZC9c6RuANn0iSQPaw9cpaLCxMOAG+yH3LzUb+c+PkHug8ww5diduQwr +dVB8bw4JQIICN3XoRXR8QRM785X+GEK7Qnk+/4aqcGzeP6rxC1mNuncj20V75JQi +cgz4++w0rQeakSjXCWL0LmgywRKJeXl2R0HgidO13piYQ9JM4cUt28mDPcly+o3E +culu9QS3Dd/MjpbYB00qQHcEgeAA3gAgJA3wcnk0NEcmAvKOCnSMmzy1acILMBR+ +Oq5bsv842pgAEDwoSveyh3N1nytSQDSuItEbmMfKCzQsHYrRe2mzwONt/AvTjKc2 +wQPLgR0wf2wz+yLyDA6kQ4KTvIOHxk7USzwk0tFZWvEDZi05viLsMwaGZ2yR4aYO +rPC0FO40oAAzQkuL+3/1ZdRPRQR4iIZB3VvKhU/bBFsHMwfUoZZMz7/YLrgsNeJH +XBjB1HtTp6keeCm3ljuVRUkSvyXFSMH0atvowRMLdFZ0TSO3SvFUTBDOTfMSWke6 +0HWcbw== +-----END TSS2 PRIVATE KEY----- + +2024/05/30 11:20:36 ======= reading key from file ======== +2024/05/30 11:20:36 primaryKey Name AAvaZWBJngiVUFq6Dg/Q7uBxAK3INE3G/GOsnm7v0TGujQ== +2024/05/30 11:20:36 Signing PEM +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1fA9hdno2ZiKHlM/wk4b +532ItD4GrzsS/y1MbBQFJ4wBFhuNnYrGF3pCWA+KynusatgSJKAULevgT0PbsGde +SD4o7Dq2fWXabn4k9jsWophuqdpNN1aMOyeYdcFkYxlujl4DuVKBkL1zpG4A2fSJ +JA9rD1ylosLEw4Ab7IfcvNRv5z4+Qe6DzDDl2J25DCt1UHxvDglAggI3dehFdHxB +Ezvzlf4YQrtCeT7/hqpwbN4/qvELWY26dyPbRXvklCJyDPj77DStB5qRKNcJYvQu +aDLBEol5eXZHQeCJ07XemJhD0kzhxS3byYM9yXL6jcRy6W71BLcN38yOltgHTSpA +dwIDAQAB +-----END PUBLIC KEY----- +TOKEN: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0IiwiZXhwIjoxNzE3MDgyNDk2fQ.Gyb8YIeQsbbl5mFVn55dO-J26HuwM1JK94RdrOEafySI7YJzfOkSeSAaSHvNR9aPiHh--nx3oMYpxPwPR161mKBF-w9DETqHn6lUqFSYzEk7tut-E1LrohrACkhSS_VbJuUw9S57imYMqzI9BTKm-FFG1mYBktWI0UWxC7e5wGaajS_cJc7fRx-5Ni-lDyBxYL1Az1ApIg9bwkEJxG7fLSI2_nsO9Unzd1mpRZ2nBUMjaK2aoG8vZMhHOK80R46VEeBq1ZT2xoaXiNZshBRf2mIptLpfSNVjT1gDCWdKVtIaBHevTpzmQLflQJVdSNKinCst-7N_QzF2UEPRBGx7GQ +2024/05/30 11:20:36 verified with TPM PublicKey +2024/05/30 11:20:36 verified with exported PubicKey +*/ +var ( + tpmPath = flag.String("tpm-path", "127.0.0.1:2321", "Path to the TPM device (character device or a Unix socket).") + out = flag.String("out", "private.pem", "privateKey File") +) + +var TPMDEVICES = []string{"/dev/tpm0", "/dev/tpmrm0"} + +func OpenTPM(path string) (io.ReadWriteCloser, error) { + if slices.Contains(TPMDEVICES, path) { + return tpmutil.OpenTPM(path) + } else if path == "simulator" { + return simulator.GetWithFixedSeedInsecure(1073741825) + } else { + return net.Dial("tcp", path) + } +} + +func main() { + + flag.Parse() + ctx := context.Background() + + log.Printf("======= Init ========") + + rwc, err := OpenTPM(*tpmPath) + if err != nil { + log.Fatalf("can't open TPM %q: %v", *tpmPath, err) + } + defer func() { + if err := rwc.Close(); err != nil { + log.Fatalf("can't close TPM %q: %v", *tpmPath, err) + } + }() + + rwr := transport.FromReadWriter(rwc) + + log.Printf("======= createPrimary ========") + + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) + if err != nil { + log.Fatalf("can't create primary %v", err) + } + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + // rsa + + log.Printf("======= create key ========") + + rsaTemplate := tpm2.TPMTPublic{ + Type: tpm2.TPMAlgRSA, + NameAlg: tpm2.TPMAlgSHA256, + ObjectAttributes: tpm2.TPMAObject{ + SignEncrypt: true, + FixedTPM: true, + FixedParent: true, + SensitiveDataOrigin: true, + UserWithAuth: true, + }, + AuthPolicy: tpm2.TPM2BDigest{}, + Parameters: tpm2.NewTPMUPublicParms( + tpm2.TPMAlgRSA, + &tpm2.TPMSRSAParms{ + Scheme: tpm2.TPMTRSAScheme{ + Scheme: tpm2.TPMAlgRSASSA, + Details: tpm2.NewTPMUAsymScheme( + tpm2.TPMAlgRSASSA, + &tpm2.TPMSSigSchemeRSASSA{ + HashAlg: tpm2.TPMAlgSHA256, + }, + ), + }, + KeyBits: 2048, + }, + ), + Unique: tpm2.NewTPMUPublicID( + tpm2.TPMAlgRSA, + &tpm2.TPM2BPublicKeyRSA{ + Buffer: make([]byte, 256), + }, + ), + } + + rsaKeyResponse, err := tpm2.CreateLoaded{ + ParentHandle: tpm2.AuthHandle{ + Handle: primaryKey.ObjectHandle, + Name: primaryKey.Name, + Auth: tpm2.PasswordAuth(nil), + }, + InPublic: tpm2.New2BTemplate(&rsaTemplate), + }.Execute(rwr) + if err != nil { + log.Fatalf("can't create rsa %v", err) + } + + // write the key to file + log.Printf("======= writing key to file ========") + + //tkf, err := keyfile.NewLoadableKey(rsaKeyResponse.OutPublic, rsaKeyResponse.OutPrivate, tpm2.TPMHandle(*persistenthandle), false) + tkf, err := keyfile.NewLoadableKey(rsaKeyResponse.OutPublic, rsaKeyResponse.OutPrivate, primaryKey.ObjectHandle, false) + if err != nil { + log.Fatalf("failed to create KeyFile: %v", err) + } + + b := new(bytes.Buffer) + + err = keyfile.Encode(b, tkf) + if err != nil { + log.Fatalf("failed to encode Key: %v", err) + } + + log.Printf("rsa Key PEM: \n%s\n", b) + + err = os.WriteFile(*out, b.Bytes(), 0644) + if err != nil { + log.Fatalf("failed to write private key to file %v", err) + } + + log.Printf("======= reading key from file ========") + c, err := os.ReadFile(*out) + if err != nil { + log.Fatalf("error reading private keyfile: %v", err) + } + key, err := keyfile.Decode(c) + if err != nil { + log.Fatalf("failed decoding key: %v", err) + } + + regenRSAKey, err := tpm2.Load{ + ParentHandle: tpm2.AuthHandle{ + Handle: primaryKey.ObjectHandle, + Name: tpm2.TPM2BName(primaryKey.Name), + Auth: tpm2.PasswordAuth(nil), + }, + InPublic: key.Pubkey, + InPrivate: key.Privkey, + }.Execute(rwr) + if err != nil { + log.Fatalf("can't load rsa key: %v", err) + } + + flush := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, err = flush.Execute(rwr) + if err != nil { + log.Fatalf("can't close primary %v", err) + } + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: regenRSAKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + claims := &jwt.RegisteredClaims{ + ExpiresAt: &jwt.NumericDate{time.Now().Add(time.Minute * 1)}, + Issuer: "test", + } + + var token *jwt.Token + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + log.Printf("primaryKey Name %s\n", base64.StdEncoding.EncodeToString(primaryKey.Name.Buffer)) + + pub, err := tpm2.ReadPublic{ + ObjectHandle: tpm2.TPMHandle(regenRSAKey.ObjectHandle), + }.Execute(rwr) + if err != nil { + log.Fatalf("error executing tpm2.ReadPublic %v", err) + } + + outPub, err := pub.OutPublic.Contents() + if err != nil { + log.Fatalf("error reading public contexts %v", err) + } + + var pubKey crypto.PublicKey + + tpmjwt.SigningMethodTPMRS256.Override() + token = jwt.NewWithClaims(tpmjwt.SigningMethodTPMRS256, claims) + + rsaDetail, err := outPub.Parameters.RSADetail() + if err != nil { + log.Fatalf("error reading rsa public %v", err) + } + rsaUnique, err := outPub.Unique.RSA() + if err != nil { + log.Fatalf("error reading rsa unique %v", err) + } + + rsaPub, err := tpm2.RSAPub(rsaDetail, rsaUnique) + if err != nil { + log.Fatalf("Failed to get rsa public key: %v", err) + } + + pubKey = rsaPub + + akBytes, err := x509.MarshalPKIXPublicKey(pubKey) + if err != nil { + log.Printf("ERROR: could not get MarshalPKIXPublicKey: %v", err) + return + } + + akPubPEM := pem.EncodeToMemory( + &pem.Block{ + Type: "PUBLIC KEY", + Bytes: akBytes, + }, + ) + log.Printf(" Signing PEM \n%s", string(akPubPEM)) + + config := &tpmjwt.TPMConfig{ + TPMDevice: rwc, + Handle: tpm2.TPMHandle(regenRSAKey.ObjectHandle), + Session: tpm2.PasswordAuth(nil), + } + + keyctx, err := tpmjwt.NewTPMContext(ctx, config) + if err != nil { + log.Fatalf("Unable to initialize tpmJWT: %v", err) + } + + // optionally set a keyID + //token.Header["kid"] = config.GetKeyID() + + tokenString, err := token.SignedString(keyctx) + if err != nil { + log.Fatalf("Error signing %v", err) + } + fmt.Printf("TOKEN: %s\n", tokenString) + + // verify with TPM based publicKey + keyFunc, err := tpmjwt.TPMVerfiyKeyfunc(ctx, config) + if err != nil { + log.Fatalf("could not get keyFunc: %v", err) + } + + vtoken, err := jwt.Parse(tokenString, keyFunc) + if err != nil { + log.Fatalf("Error verifying token %v", err) + } + if vtoken.Valid { + log.Println(" verified with TPM PublicKey") + } + + // verify with provided RSAPublic key + pubKeyr := config.GetPublicKey() + + v, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + return pubKeyr, nil + }) + if err != nil { + log.Fatalf("Error parsing token %v", err) + } + if v.Valid { + log.Println(" verified with exported PubicKey") + } +} diff --git a/example/go_tpm_tools_compat/main.go b/example/go_tpm_tools_compat/main.go new file mode 100644 index 0000000..7bdcfb1 --- /dev/null +++ b/example/go_tpm_tools_compat/main.go @@ -0,0 +1,196 @@ +package main + +import ( + "context" + "crypto" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "flag" + "fmt" + "io" + "log" + "net" + "slices" + "time" + + jwt "github.com/golang-jwt/jwt/v5" + "github.com/google/go-tpm-tools/client" + "github.com/google/go-tpm-tools/simulator" + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpm2/transport" + "github.com/google/go-tpm/tpmutil" + tpmjwt "github.com/salrashid123/golang-jwt-tpm" +) + +/* + loads a key using go-tpm-tools + + Note, go-tpm-tools based keys only supports a limited set of authorizations (PCR and EKSession) + https://pkg.go.dev/github.com/google/go-tpm-tools/client#Session + + +*/ + +var ( + tpmPath = flag.String("tpm-path", "127.0.0.1:2321", "Path to the TPM device (character device or a Unix socket).") + persistentHandle = flag.Uint("persistentHandle", 0x81008001, "Handle value") +) + +var TPMDEVICES = []string{"/dev/tpm0", "/dev/tpmrm0"} + +func OpenTPM(path string) (io.ReadWriteCloser, error) { + if slices.Contains(TPMDEVICES, path) { + return tpmutil.OpenTPM(path) + } else if path == "simulator" { + return simulator.GetWithFixedSeedInsecure(1073741825) + } else { + return net.Dial("tcp", path) + } +} + +func main() { + + flag.Parse() + ctx := context.Background() + + log.Printf("======= Init ========") + + rwc, err := OpenTPM(*tpmPath) + if err != nil { + log.Fatalf("can't open TPM %q: %v", *tpmPath, err) + } + defer func() { + if err := rwc.Close(); err != nil { + log.Fatalf("can't close TPM %q: %v", *tpmPath, err) + } + }() + + rwr := transport.FromReadWriter(rwc) + + claims := &jwt.RegisteredClaims{ + ExpiresAt: &jwt.NumericDate{time.Now().Add(time.Minute * 1)}, + Issuer: "test", + } + + var token *jwt.Token + + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) + if err != nil { + log.Fatalf("can't create primary %v", err) + } + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + log.Printf("primaryKey Name %s\n", base64.StdEncoding.EncodeToString(primaryKey.Name.Buffer)) + + // load a key using go-tpm-tools + k, err := client.LoadCachedKey(rwc, tpmutil.Handle(*persistentHandle), nil) + if err != nil { + log.Printf("ERROR: could not initialize Key: %v", err) + return + } + defer k.Close() + + pub, err := tpm2.ReadPublic{ + ObjectHandle: tpm2.TPMHandle(k.Handle().HandleValue()), + }.Execute(rwr) + if err != nil { + log.Fatalf("error executing tpm2.ReadPublic %v", err) + } + + outPub, err := pub.OutPublic.Contents() + if err != nil { + log.Fatalf("error reading public contexts %v", err) + } + + var pubKey crypto.PublicKey + + tpmjwt.SigningMethodTPMRS256.Override() + token = jwt.NewWithClaims(tpmjwt.SigningMethodTPMRS256, claims) + + rsaDetail, err := outPub.Parameters.RSADetail() + if err != nil { + log.Fatalf("error reading rsa public %v", err) + } + rsaUnique, err := outPub.Unique.RSA() + if err != nil { + log.Fatalf("error reading rsa unique %v", err) + } + + rsaPub, err := tpm2.RSAPub(rsaDetail, rsaUnique) + if err != nil { + log.Fatalf("Failed to get rsa public key: %v", err) + } + + pubKey = rsaPub + + akBytes, err := x509.MarshalPKIXPublicKey(pubKey) + if err != nil { + log.Printf("ERROR: could not get MarshalPKIXPublicKey: %v", err) + return + } + + akPubPEM := pem.EncodeToMemory( + &pem.Block{ + Type: "PUBLIC KEY", + Bytes: akBytes, + }, + ) + log.Printf(" Signing PEM \n%s", string(akPubPEM)) + + config := &tpmjwt.TPMConfig{ + TPMDevice: rwc, + Handle: tpm2.TPMHandle(*persistentHandle), + Session: tpm2.PasswordAuth(nil), + } + + keyctx, err := tpmjwt.NewTPMContext(ctx, config) + if err != nil { + log.Fatalf("Unable to initialize tpmJWT: %v", err) + } + + // optionally set a keyID + //token.Header["kid"] = config.GetKeyID() + + tokenString, err := token.SignedString(keyctx) + if err != nil { + log.Fatalf("Error signing %v", err) + } + fmt.Printf("TOKEN: %s\n", tokenString) + + // verify with TPM based publicKey + keyFunc, err := tpmjwt.TPMVerfiyKeyfunc(ctx, config) + if err != nil { + log.Fatalf("could not get keyFunc: %v", err) + } + + vtoken, err := jwt.Parse(tokenString, keyFunc) + if err != nil { + log.Fatalf("Error verifying token %v", err) + } + if vtoken.Valid { + log.Println(" verified with TPM PublicKey") + } + + // verify with provided RSAPublic key + pubKeyr := config.GetPublicKey() + + v, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + return pubKeyr, nil + }) + if err != nil { + log.Fatalf("Error parsing token %v", err) + } + if v.Valid { + log.Println(" verified with exported PubicKey") + } +} diff --git a/example/nopolicy/main.go b/example/nopolicy/main.go index a0c00a7..8112ed1 100644 --- a/example/nopolicy/main.go +++ b/example/nopolicy/main.go @@ -3,48 +3,43 @@ package main import ( "context" "crypto" - "crypto/ecdsa" - "crypto/rsa" "crypto/x509" + "encoding/base64" "encoding/pem" "flag" "fmt" + "io" "log" + "net" + "slices" "time" jwt "github.com/golang-jwt/jwt/v5" - "github.com/google/go-tpm-tools/client" - "github.com/google/go-tpm/legacy/tpm2" + "github.com/google/go-tpm-tools/simulator" + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpm2/transport" "github.com/google/go-tpm/tpmutil" tpmjwt "github.com/salrashid123/golang-jwt-tpm" ) -/* -## RSA - tpm2_createprimary -C e -c primary.ctx - tpm2_create -G rsa2048:rsassa:null -g sha256 -u key.pub -r key.priv -C primary.ctx - tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx - tpm2_evictcontrol -C o -c key.ctx 0x81008001 - -## RSA-PSS - tpm2_createprimary -C e -c primary.ctx - tpm2_create -G rsa2048:rsapss:null -g sha256 -u key.pub -r key.priv -C primary.ctx - tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx - tpm2_evictcontrol -C o -c key.ctx 0x81008001 - -## ECC - tpm2_createprimary -C e -c primary.ctx - tpm2_create -G ecc:ecdsa -g sha256 -u key.pub -r key.priv -C primary.ctx - tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx - tpm2_evictcontrol -C o -c key.ctx 0x81008002 -*/ - var ( - tpmPath = flag.String("tpm-path", "/dev/tpm0", "Path to the TPM device (character device or a Unix socket).") + tpmPath = flag.String("tpm-path", "127.0.0.1:2321", "Path to the TPM device (character device or a Unix socket).") persistentHandle = flag.Uint("persistentHandle", 0x81008001, "Handle value") mode = flag.String("mode", "rsa", "which test to run: rsa, rsapss or ecc") ) +var TPMDEVICES = []string{"/dev/tpm0", "/dev/tpmrm0"} + +func OpenTPM(path string) (io.ReadWriteCloser, error) { + if slices.Contains(TPMDEVICES, path) { + return tpmutil.OpenTPM(path) + } else if path == "simulator" { + return simulator.GetWithFixedSeedInsecure(1073741825) + } else { + return net.Dial("tcp", path) + } +} + func main() { flag.Parse() @@ -52,7 +47,7 @@ func main() { log.Printf("======= Init ========") - rwc, err := tpm2.OpenTPM(*tpmPath) + rwc, err := OpenTPM(*tpmPath) if err != nil { log.Fatalf("can't open TPM %q: %v", *tpmPath, err) } @@ -62,6 +57,8 @@ func main() { } }() + rwr := transport.FromReadWriter(rwc) + claims := &jwt.RegisteredClaims{ ExpiresAt: &jwt.NumericDate{time.Now().Add(time.Minute * 1)}, Issuer: "test", @@ -69,12 +66,34 @@ func main() { var token *jwt.Token - k, err := client.LoadCachedKey(rwc, tpmutil.Handle(*persistentHandle), nil) + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) if err != nil { - log.Printf("ERROR: could not initialize Key: %v", err) - return + log.Fatalf("can't create primary %v", err) + } + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + log.Printf("primaryKey Name %s\n", base64.StdEncoding.EncodeToString(primaryKey.Name.Buffer)) + + pub, err := tpm2.ReadPublic{ + ObjectHandle: tpm2.TPMHandle(*persistentHandle), + }.Execute(rwr) + if err != nil { + log.Fatalf("error executing tpm2.ReadPublic %v", err) + } + + outPub, err := pub.OutPublic.Contents() + if err != nil { + log.Fatalf("error reading public contexts %v", err) } - defer k.Close() var pubKey crypto.PublicKey switch *mode { @@ -82,19 +101,64 @@ func main() { tpmjwt.SigningMethodTPMRS256.Override() token = jwt.NewWithClaims(tpmjwt.SigningMethodTPMRS256, claims) - pubKey = k.PublicKey().(*rsa.PublicKey) + rsaDetail, err := outPub.Parameters.RSADetail() + if err != nil { + log.Fatalf("error reading rsa public %v", err) + } + rsaUnique, err := outPub.Unique.RSA() + if err != nil { + log.Fatalf("error reading rsa unique %v", err) + } + + rsaPub, err := tpm2.RSAPub(rsaDetail, rsaUnique) + if err != nil { + log.Fatalf("Failed to get rsa public key: %v", err) + } + + pubKey = rsaPub case "rsapss": tpmjwt.SigningMethodTPMPS256.Override() token = jwt.NewWithClaims(tpmjwt.SigningMethodTPMPS256, claims) - pubKey = k.PublicKey().(*rsa.PublicKey) + rsaDetail, err := outPub.Parameters.RSADetail() + if err != nil { + log.Fatalf("error reading rsa public %v", err) + } + rsaUnique, err := outPub.Unique.RSA() + if err != nil { + log.Fatalf("error reading rsa unique %v", err) + } + + rsaPub, err := tpm2.RSAPub(rsaDetail, rsaUnique) + if err != nil { + log.Fatalf("Failed to get rsa public key: %v", err) + } + + pubKey = rsaPub case "ecc": tpmjwt.SigningMethodTPMES256.Override() token = jwt.NewWithClaims(tpmjwt.SigningMethodTPMES256, claims) - pubKey = k.PublicKey().(*ecdsa.PublicKey) + eccDetail, err := outPub.Parameters.ECCDetail() + if err != nil { + log.Fatalf("error reading ec details %v", err) + } + ecUnique, err := outPub.Unique.ECC() + if err != nil { + log.Fatalf("error reading ec unique %v", err) + } + + crv, err := eccDetail.CurveID.ECDHCurve() + if err != nil { + log.Fatalf("error reading ec curve %v", err) + } + pubKey, err = tpm2.ECDHPubKey(crv, ecUnique) + if err != nil { + log.Fatalf("Failed to get ecc public key: %v", err) + } + default: - log.Printf("ERROR: unsupported key type: %v", k.PublicKey()) + log.Printf(" unsupported mode %s", *mode) return } @@ -114,7 +178,8 @@ func main() { config := &tpmjwt.TPMConfig{ TPMDevice: rwc, - Key: k, + Handle: tpm2.TPMHandle(*persistentHandle), + //Session: tpm2.PasswordAuth(nil), } keyctx, err := tpmjwt.NewTPMContext(ctx, config) @@ -148,7 +213,7 @@ func main() { // verify with provided RSAPublic key pubKeyr := config.GetPublicKey() - v, err := jwt.Parse(vtoken.Raw, func(token *jwt.Token) (interface{}, error) { + v, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { return pubKeyr, nil }) if err != nil { diff --git a/example/policy/main.go b/example/policy/main.go deleted file mode 100644 index 8e7f7ab..0000000 --- a/example/policy/main.go +++ /dev/null @@ -1,95 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "log" - "time" - - jwt "github.com/golang-jwt/jwt/v5" - "github.com/google/go-tpm-tools/client" - "github.com/google/go-tpm/legacy/tpm2" - "github.com/google/go-tpm/tpmutil" - tpmjwt "github.com/salrashid123/golang-jwt-tpm" -) - -/* -tpm2_pcrread sha256:23 -tpm2_startauthsession -S session.dat -tpm2_policypcr -S session.dat -l sha256:23 -L policy.dat -tpm2_flushcontext session.dat -tpm2_createprimary -C o -c primary2.ctx -tpm2_create -G rsa2048:rsassa:null -g sha256 -u rsa2.pub -r rsa2.priv -C primary2.ctx -L policy.dat -tpm2_load -C primary2.ctx -u rsa2.pub -r rsa2.priv -c rsa2.ctx -tpm2_evictcontrol -C o -c rsa2.ctx 0x81008004 -*/ - -var ( - tpmPath = flag.String("tpm-path", "/dev/tpm0", "Path to the TPM device (character device or a Unix socket).") - persistentHandle = flag.Uint("persistentHandle", 0x81008004, "Handle value") - pcr = flag.Int("pcr", 23, "PCR Bound value") -) - -func main() { - - flag.Parse() - ctx := context.Background() - - rwc, err := tpm2.OpenTPM(*tpmPath) - if err != nil { - log.Fatalf("can't open TPM %q: %v", *tpmPath, err) - } - defer func() { - if err := rwc.Close(); err != nil { - log.Fatalf("can't close TPM %q: %v", *tpmPath, err) - } - }() - - claims := &jwt.RegisteredClaims{ - ExpiresAt: &jwt.NumericDate{time.Now().Add(time.Minute * 1)}, - Issuer: "test", - } - - tpmjwt.SigningMethodTPMRS256.Override() - token := jwt.NewWithClaims(tpmjwt.SigningMethodTPMRS256, claims) - - s, err := client.NewPCRSession(rwc, tpm2.PCRSelection{tpm2.AlgSHA256, []int{*pcr}}) - if err != nil { - log.Fatalf("Unable to initialize tpmJWT: %v", err) - } - defer s.Close() - sessionKey, err := client.LoadCachedKey(rwc, tpmutil.Handle(*persistentHandle), s) - if err != nil { - log.Fatalf("Unable to Load Key: %v", err) - } - defer sessionKey.Close() - sessionConfig := &tpmjwt.TPMConfig{ - TPMDevice: rwc, - Key: sessionKey, - } - sessionKeyctx, err := tpmjwt.NewTPMContext(ctx, sessionConfig) - if err != nil { - log.Fatalf("Unable to initialize tpmJWT: %v", err) - } - - sessionTokenString, err := token.SignedString(sessionKeyctx) - if err != nil { - log.Fatalf("Error signing %v", err) - } - fmt.Printf("TOKEN: %s\n", sessionTokenString) - - sessionKeyFunc, err := tpmjwt.TPMVerfiyKeyfunc(ctx, sessionConfig) - if err != nil { - log.Fatalf("could not get keyFunc: %v", err) - } - - sessionVtoken, err := jwt.Parse(sessionTokenString, sessionKeyFunc) - if err != nil { - log.Fatalf("Error verifying token %v", err) - } - if sessionVtoken.Valid { - log.Println(" verified with TPM PublicKey") - } - -} diff --git a/example/policy_password/main.go b/example/policy_password/main.go new file mode 100644 index 0000000..b81963f --- /dev/null +++ b/example/policy_password/main.go @@ -0,0 +1,187 @@ +package main + +import ( + "context" + "crypto" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "flag" + "fmt" + "io" + "log" + "net" + "slices" + "time" + + jwt "github.com/golang-jwt/jwt/v5" + "github.com/google/go-tpm-tools/simulator" + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpm2/transport" + "github.com/google/go-tpm/tpmutil" + tpmjwt "github.com/salrashid123/golang-jwt-tpm" +) + +var ( + tpmPath = flag.String("tpm-path", "127.0.0.1:2321", "Path to the TPM device (character device or a Unix socket).") + persistentHandle = flag.Uint("persistentHandle", 0x81008001, "Handle value") +) + +var TPMDEVICES = []string{"/dev/tpm0", "/dev/tpmrm0"} + +func OpenTPM(path string) (io.ReadWriteCloser, error) { + if slices.Contains(TPMDEVICES, path) { + return tpmutil.OpenTPM(path) + } else if path == "simulator" { + return simulator.GetWithFixedSeedInsecure(1073741825) + } else { + return net.Dial("tcp", path) + } +} + +func main() { + + flag.Parse() + ctx := context.Background() + + log.Printf("======= Init ========") + + rwc, err := OpenTPM(*tpmPath) + if err != nil { + log.Fatalf("can't open TPM %q: %v", *tpmPath, err) + } + defer func() { + if err := rwc.Close(); err != nil { + log.Fatalf("can't close TPM %q: %v", *tpmPath, err) + } + }() + + rwr := transport.FromReadWriter(rwc) + + claims := &jwt.RegisteredClaims{ + ExpiresAt: &jwt.NumericDate{time.Now().Add(time.Minute * 1)}, + Issuer: "test", + } + + var token *jwt.Token + + rootPass := []byte("pass1") + keyPass := []byte("pass2") + + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + InSensitive: tpm2.TPM2BSensitiveCreate{ + Sensitive: &tpm2.TPMSSensitiveCreate{ + UserAuth: tpm2.TPM2BAuth{ + Buffer: rootPass, + }, + }, + }, + }.Execute(rwr) + if err != nil { + log.Fatalf("can't create primary %v", err) + } + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + log.Printf("primaryKey Name %s\n", base64.StdEncoding.EncodeToString(primaryKey.Name.Buffer)) + + pub, err := tpm2.ReadPublic{ + ObjectHandle: tpm2.TPMHandle(*persistentHandle), + }.Execute(rwr) + if err != nil { + log.Fatalf("tpmjwt: error executing tpm2.ReadPublic %v", err) + } + + outPub, err := pub.OutPublic.Contents() + if err != nil { + log.Fatalf("tpmjwt: error reading public contexts %v", err) + } + var pubKey crypto.PublicKey + + tpmjwt.SigningMethodTPMRS256.Override() + token = jwt.NewWithClaims(tpmjwt.SigningMethodTPMRS256, claims) + + rsaDetail, err := outPub.Parameters.RSADetail() + if err != nil { + log.Fatalf("tpmjwt: error reading rsa public %v", err) + } + rsaUnique, err := outPub.Unique.RSA() + if err != nil { + log.Fatalf("tpmjwt: error reading rsa unique %v", err) + } + + rsaPub, err := tpm2.RSAPub(rsaDetail, rsaUnique) + if err != nil { + log.Fatalf("Failed to get rsa public key: %v", err) + } + + pubKey = rsaPub + + akBytes, err := x509.MarshalPKIXPublicKey(pubKey) + if err != nil { + log.Printf("ERROR: could not get MarshalPKIXPublicKey: %v", err) + return + } + + akPubPEM := pem.EncodeToMemory( + &pem.Block{ + Type: "PUBLIC KEY", + Bytes: akBytes, + }, + ) + log.Printf(" Signing PEM \n%s", string(akPubPEM)) + + config := &tpmjwt.TPMConfig{ + TPMDevice: rwc, + Handle: tpm2.TPMHandle(*persistentHandle), + Session: tpm2.PasswordAuth(keyPass), + } + + keyctx, err := tpmjwt.NewTPMContext(ctx, config) + if err != nil { + log.Fatalf("Unable to initialize tpmJWT: %v", err) + } + + // optionally set a keyID + //token.Header["kid"] = config.GetKeyID() + + tokenString, err := token.SignedString(keyctx) + if err != nil { + log.Fatalf("Error signing %v", err) + } + fmt.Printf("TOKEN: %s\n", tokenString) + + // verify with TPM based publicKey + keyFunc, err := tpmjwt.TPMVerfiyKeyfunc(ctx, config) + if err != nil { + log.Fatalf("could not get keyFunc: %v", err) + } + + vtoken, err := jwt.Parse(tokenString, keyFunc) + if err != nil { + log.Fatalf("Error verifying token %v", err) + } + if vtoken.Valid { + log.Println(" verified with TPM PublicKey") + } + + // verify with provided RSAPublic key + pubKeyr := config.GetPublicKey() + + v, err := jwt.Parse(vtoken.Raw, func(token *jwt.Token) (interface{}, error) { + return pubKeyr, nil + }) + if err != nil { + log.Fatalf("Error parsing token %v", err) + } + if v.Valid { + log.Println(" verified with exported PubicKey") + } +} diff --git a/example/policy_pcr/main.go b/example/policy_pcr/main.go new file mode 100644 index 0000000..bae9d81 --- /dev/null +++ b/example/policy_pcr/main.go @@ -0,0 +1,199 @@ +package main + +import ( + "context" + "crypto" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "flag" + "fmt" + "io" + "log" + "net" + "slices" + "time" + + jwt "github.com/golang-jwt/jwt/v5" + "github.com/google/go-tpm-tools/simulator" + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpm2/transport" + "github.com/google/go-tpm/tpmutil" + tpmjwt "github.com/salrashid123/golang-jwt-tpm" +) + +var ( + tpmPath = flag.String("tpm-path", "127.0.0.1:2321", "Path to the TPM device (character device or a Unix socket).") + persistentHandle = flag.Uint("persistentHandle", 0x81008003, "Handle value") +) + +var TPMDEVICES = []string{"/dev/tpm0", "/dev/tpmrm0"} + +func OpenTPM(path string) (io.ReadWriteCloser, error) { + if slices.Contains(TPMDEVICES, path) { + return tpmutil.OpenTPM(path) + } else if path == "simulator" { + return simulator.GetWithFixedSeedInsecure(1073741825) + } else { + return net.Dial("tcp", path) + } +} + +func main() { + + flag.Parse() + ctx := context.Background() + + log.Printf("======= Init ========") + + rwc, err := OpenTPM(*tpmPath) + if err != nil { + log.Fatalf("can't open TPM %q: %v", *tpmPath, err) + } + defer func() { + if err := rwc.Close(); err != nil { + log.Fatalf("can't close TPM %q: %v", *tpmPath, err) + } + }() + + rwr := transport.FromReadWriter(rwc) + + claims := &jwt.RegisteredClaims{ + ExpiresAt: &jwt.NumericDate{time.Now().Add(time.Minute * 1)}, + Issuer: "test", + } + + var token *jwt.Token + + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) + if err != nil { + log.Fatalf("can't create primary %v", err) + } + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + log.Printf("primaryKey Name %s\n", base64.StdEncoding.EncodeToString(primaryKey.Name.Buffer)) + + pub, err := tpm2.ReadPublic{ + ObjectHandle: tpm2.TPMHandle(*persistentHandle), + }.Execute(rwr) + if err != nil { + log.Fatalf("tpmjwt: error executing tpm2.ReadPublic %v", err) + } + + outPub, err := pub.OutPublic.Contents() + if err != nil { + log.Fatalf("tpmjwt: error reading public contexts %v", err) + } + var pubKey crypto.PublicKey + + tpmjwt.SigningMethodTPMRS256.Override() + token = jwt.NewWithClaims(tpmjwt.SigningMethodTPMRS256, claims) + + rsaDetail, err := outPub.Parameters.RSADetail() + if err != nil { + log.Fatalf("tpmjwt: error reading rsa public %v", err) + } + rsaUnique, err := outPub.Unique.RSA() + if err != nil { + log.Fatalf("tpmjwt: error reading rsa unique %v", err) + } + + rsaPub, err := tpm2.RSAPub(rsaDetail, rsaUnique) + if err != nil { + log.Fatalf("Failed to get rsa public key: %v", err) + } + + pubKey = rsaPub + + akBytes, err := x509.MarshalPKIXPublicKey(pubKey) + if err != nil { + log.Printf("ERROR: could not get MarshalPKIXPublicKey: %v", err) + return + } + + akPubPEM := pem.EncodeToMemory( + &pem.Block{ + Type: "PUBLIC KEY", + Bytes: akBytes, + }, + ) + log.Printf(" Signing PEM \n%s", string(akPubPEM)) + + sess, cleanup, err := tpm2.PolicySession(rwr, tpm2.TPMAlgSHA256, 16) + if err != nil { + log.Printf("ERROR: could not get PolicySession: %v", err) + return + } + defer cleanup() + + _, err = tpm2.PolicyPCR{ + PolicySession: sess.Handle(), + Pcrs: tpm2.TPMLPCRSelection{ + PCRSelections: []tpm2.TPMSPCRSelection{ + { + Hash: tpm2.TPMAlgSHA256, + PCRSelect: tpm2.PCClientCompatible.PCRs(23), + }, + }, + }, + }.Execute(rwr) + if err != nil { + log.Fatalf("Unable to initialize tpmJWT: %v", err) + } + + config := &tpmjwt.TPMConfig{ + TPMDevice: rwc, + Handle: tpm2.TPMHandle(*persistentHandle), + Session: sess, + } + + keyctx, err := tpmjwt.NewTPMContext(ctx, config) + if err != nil { + log.Fatalf("Unable to initialize tpmJWT: %v", err) + } + + // optionally set a keyID + //token.Header["kid"] = config.GetKeyID() + + tokenString, err := token.SignedString(keyctx) + if err != nil { + log.Fatalf("Error signing %v", err) + } + fmt.Printf("TOKEN: %s\n", tokenString) + + // verify with TPM based publicKey + keyFunc, err := tpmjwt.TPMVerfiyKeyfunc(ctx, config) + if err != nil { + log.Fatalf("could not get keyFunc: %v", err) + } + + vtoken, err := jwt.Parse(tokenString, keyFunc) + if err != nil { + log.Fatalf("Error verifying token %v", err) + } + if vtoken.Valid { + log.Println(" verified with TPM PublicKey") + } + + // verify with provided RSAPublic key + pubKeyr := config.GetPublicKey() + + v, err := jwt.Parse(vtoken.Raw, func(token *jwt.Token) (interface{}, error) { + return pubKeyr, nil + }) + if err != nil { + log.Fatalf("Error parsing token %v", err) + } + if v.Valid { + log.Println(" verified with exported PubicKey") + } +} diff --git a/example/session_encryption/main.go b/example/session_encryption/main.go new file mode 100644 index 0000000..24ba2c9 --- /dev/null +++ b/example/session_encryption/main.go @@ -0,0 +1,201 @@ +package main + +import ( + "context" + "crypto" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "flag" + "fmt" + "io" + "log" + "net" + "slices" + "time" + + jwt "github.com/golang-jwt/jwt/v5" + "github.com/google/go-tpm-tools/simulator" + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpm2/transport" + "github.com/google/go-tpm/tpmutil" + tpmjwt "github.com/salrashid123/golang-jwt-tpm" +) + +var ( + tpmPath = flag.String("tpm-path", "127.0.0.1:2321", "Path to the TPM device (character device or a Unix socket).") + persistentHandle = flag.Uint("persistentHandle", 0x81008001, "Handle value") +) + +var TPMDEVICES = []string{"/dev/tpm0", "/dev/tpmrm0"} + +func OpenTPM(path string) (io.ReadWriteCloser, error) { + if slices.Contains(TPMDEVICES, path) { + return tpmutil.OpenTPM(path) + } else if path == "simulator" { + return simulator.GetWithFixedSeedInsecure(1073741825) + } else { + return net.Dial("tcp", path) + } +} + +func main() { + + flag.Parse() + ctx := context.Background() + + log.Printf("======= Init ========") + + rwc, err := OpenTPM(*tpmPath) + if err != nil { + log.Fatalf("can't open TPM %q: %v", *tpmPath, err) + } + defer func() { + if err := rwc.Close(); err != nil { + log.Fatalf("can't close TPM %q: %v", *tpmPath, err) + } + }() + + rwr := transport.FromReadWriter(rwc) + + claims := &jwt.RegisteredClaims{ + ExpiresAt: &jwt.NumericDate{time.Now().Add(time.Minute * 1)}, + Issuer: "test", + } + + var token *jwt.Token + + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) + if err != nil { + log.Fatalf("can't create primary %v", err) + } + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + createEKCmd := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHEndorsement, + InPublic: tpm2.New2B(tpm2.RSAEKTemplate), + } + createEKRsp, err := createEKCmd.Execute(rwr) + if err != nil { + log.Fatalf("can't acquire acquire ek %v", err) + } + encryptionPub, err := createEKRsp.OutPublic.Contents() + if err != nil { + log.Fatalf("can't create ekpub blob %v", err) + } + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: createEKRsp.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + log.Printf("primaryKey Name %s\n", base64.StdEncoding.EncodeToString(primaryKey.Name.Buffer)) + + pub, err := tpm2.ReadPublic{ + ObjectHandle: tpm2.TPMHandle(*persistentHandle), + }.Execute(rwr) + if err != nil { + log.Fatalf("error executing tpm2.ReadPublic %v", err) + } + + outPub, err := pub.OutPublic.Contents() + if err != nil { + log.Fatalf("error reading public contexts %v", err) + } + + var pubKey crypto.PublicKey + + tpmjwt.SigningMethodTPMRS256.Override() + token = jwt.NewWithClaims(tpmjwt.SigningMethodTPMRS256, claims) + + rsaDetail, err := outPub.Parameters.RSADetail() + if err != nil { + log.Fatalf("error reading rsa public %v", err) + } + rsaUnique, err := outPub.Unique.RSA() + if err != nil { + log.Fatalf("error reading rsa unique %v", err) + } + + rsaPub, err := tpm2.RSAPub(rsaDetail, rsaUnique) + if err != nil { + log.Fatalf("Failed to get rsa public key: %v", err) + } + + pubKey = rsaPub + + akBytes, err := x509.MarshalPKIXPublicKey(pubKey) + if err != nil { + log.Printf("ERROR: could not get MarshalPKIXPublicKey: %v", err) + return + } + + akPubPEM := pem.EncodeToMemory( + &pem.Block{ + Type: "PUBLIC KEY", + Bytes: akBytes, + }, + ) + log.Printf(" Signing PEM \n%s", string(akPubPEM)) + + config := &tpmjwt.TPMConfig{ + TPMDevice: rwc, + Handle: tpm2.TPMHandle(*persistentHandle), + EncryptionHandle: createEKRsp.ObjectHandle, + EncryptionPub: encryptionPub, + //Session: tpm2.PasswordAuth(nil), + } + + keyctx, err := tpmjwt.NewTPMContext(ctx, config) + if err != nil { + log.Fatalf("Unable to initialize tpmJWT: %v", err) + } + + // optionally set a keyID + //token.Header["kid"] = config.GetKeyID() + + tokenString, err := token.SignedString(keyctx) + if err != nil { + log.Fatalf("Error signing %v", err) + } + fmt.Printf("TOKEN: %s\n", tokenString) + + // verify with TPM based publicKey + keyFunc, err := tpmjwt.TPMVerfiyKeyfunc(ctx, config) + if err != nil { + log.Fatalf("could not get keyFunc: %v", err) + } + + vtoken, err := jwt.Parse(tokenString, keyFunc) + if err != nil { + log.Fatalf("Error verifying token %v", err) + } + if vtoken.Valid { + log.Println(" verified with TPM PublicKey") + } + + // verify with provided RSAPublic key + pubKeyr := config.GetPublicKey() + + v, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + return pubKeyr, nil + }) + if err != nil { + log.Fatalf("Error parsing token %v", err) + } + if v.Valid { + log.Println(" verified with exported PubicKey") + } + +} diff --git a/example/util/keycreate.go b/example/util/keycreate.go deleted file mode 100644 index 59eee77..0000000 --- a/example/util/keycreate.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2020 Google LLC. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "encoding/base64" - "encoding/hex" - "encoding/json" - "encoding/pem" - "fmt" - "os" - - "crypto/sha256" - "crypto/x509" - "flag" - - "log" - - "github.com/google/go-tpm-tools/client" - "github.com/google/go-tpm/legacy/tpm2" - "github.com/google/go-tpm/tpmutil" - "github.com/lestrrat-go/jwx/v2/jwk" -) - -const () - -var () - -var ( - tpmPath = flag.String("tpm-path", "/dev/tpm0", "Path to the TPM device (character device or a Unix socket).") - keyAlg = flag.String("keyAlg", "RSA", "Key Algorithm") - publicKeyFile = flag.String("publicKeyFile", "key.pem", "PEM File to write the public key") - persistentHandle = flag.Uint("persistentHandle", 0x81008001, "Handle value") - flushHandles = flag.Bool("flushHandles", false, "FlushTPM Hanldles") - evict = flag.Bool("evict", false, "Evict prior handle") - handleNames = map[string][]tpm2.HandleType{ - "all": {tpm2.HandleTypeLoadedSession, tpm2.HandleTypeSavedSession, tpm2.HandleTypeTransient}, - "loaded": {tpm2.HandleTypeLoadedSession}, - "saved": {tpm2.HandleTypeSavedSession}, - "transient": {tpm2.HandleTypeTransient}, - "none": {}, - } - // using attestation key - keyParametersRSA = client.AKTemplateRSA() - keyParametersECC = client.AKTemplateECC() - - // using unrestricted key - keyParametersImported = tpm2.Public{ - Type: tpm2.AlgRSA, - NameAlg: tpm2.AlgSHA256, - Attributes: tpm2.FlagUserWithAuth | tpm2.FlagSign, - AuthPolicy: []byte{}, - RSAParameters: &tpm2.RSAParams{ - Sign: &tpm2.SigScheme{ - Alg: tpm2.AlgRSASSA, - Hash: tpm2.AlgSHA256, - }, - KeyBits: 2048, - }, - } - - keyParametersCreatedRSA = tpm2.Public{ - Type: tpm2.AlgRSA, - NameAlg: tpm2.AlgSHA256, - Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | - tpm2.FlagUserWithAuth | tpm2.FlagSign, - AuthPolicy: []byte{}, - RSAParameters: &tpm2.RSAParams{ - Sign: &tpm2.SigScheme{ - Alg: tpm2.AlgRSASSA, - Hash: tpm2.AlgSHA256, - }, - KeyBits: 2048, - }, - } - - keyParametersCreatedECC = tpm2.Public{ - Type: tpm2.AlgECC, - NameAlg: tpm2.AlgSHA256, - Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | - tpm2.FlagUserWithAuth | tpm2.FlagSign, - ECCParameters: &tpm2.ECCParams{ - Sign: &tpm2.SigScheme{Alg: tpm2.AlgECDSA, Hash: tpm2.AlgSHA256}, - CurveID: tpm2.CurveNISTP256, - Point: tpm2.ECPoint{ - XRaw: make([]byte, 32), - YRaw: make([]byte, 32), - }, - }, - } -) - -func main() { - - flag.Parse() - log.Printf("======= Init ========") - - rwc, err := tpm2.OpenTPM(*tpmPath) - if err != nil { - log.Fatalf("can't open TPM %q: %v", *tpmPath, err) - } - defer func() { - if err := rwc.Close(); err != nil { - log.Fatalf("can't close TPM %q: %v", *tpmPath, err) - } - }() - - if *flushHandles { - totalHandles := 0 - for _, handleType := range handleNames["all"] { - handles, err := client.Handles(rwc, handleType) - if err != nil { - log.Fatalf("getting handles: %v", err) - } - for _, handle := range handles { - if err = tpm2.FlushContext(rwc, handle); err != nil { - log.Fatalf("flushing handle 0x%x: %v", handle, err) - } - log.Printf("Handle 0x%x flushed\n", handle) - totalHandles++ - } - } - log.Printf("%d handles flushed\n", totalHandles) - } - - var k *client.Key - - if *keyAlg == "RSA" { - k, err = client.NewKey(rwc, tpm2.HandleOwner, keyParametersRSA) - } else if *keyAlg == "ECC" { - k, err = client.NewKey(rwc, tpm2.HandleOwner, keyParametersECC) - } else { - log.Fatalf("unknown key parameter handles: %s", *keyAlg) - } - if err != nil { - log.Fatalf("can't create SRK %q: %v", *tpmPath, err) - } - - log.Printf(" key Name: \n%s", hex.EncodeToString(k.Name().Digest.Value)) - - kh := k.Handle() - log.Printf("======= PersistHandle (k) ========") - pHandle := tpmutil.Handle(*persistentHandle) - // if you want to evict an existing - if *evict { - err = tpm2.EvictControl(rwc, "", tpm2.HandleOwner, pHandle, pHandle) - if err != nil { - log.Fatalf(" Unable evict persistentHandle: %v ", err) - } - } - - err = tpm2.EvictControl(rwc, "", tpm2.HandleOwner, kh, pHandle) - if err != nil { - log.Fatalf(" Unable to set persistentHandle: %v", err) - } - defer tpm2.FlushContext(rwc, kh) - - kPublicKey, _, _, err := tpm2.ReadPublic(rwc, kh) - if err != nil { - log.Fatalf("Error tpmEkPub.Key() failed: %s", err) - } - - ap, err := kPublicKey.Key() - if err != nil { - log.Fatalf("reading Key() failed: %s", err) - } - akBytes, err := x509.MarshalPKIXPublicKey(ap) - if err != nil { - log.Fatalf("Unable to convert ekpub: %v", err) - } - - rakPubPEM := pem.EncodeToMemory( - &pem.Block{ - Type: "PUBLIC KEY", - Bytes: akBytes, - }, - ) - log.Printf(" PublicKey: \n%v", string(rakPubPEM)) - - err = os.WriteFile(*publicKeyFile, rakPubPEM, 0644) - if err != nil { - log.Fatalf("Could not write file %v", err) - } - log.Printf("Public Key written to: %s", *publicKeyFile) - - der, err := x509.MarshalPKIXPublicKey(ap) - if err != nil { - log.Fatalf("keycreate: error converting public key: %v", err) - } - hasher := sha256.New() - hasher.Write(der) - kid := base64.RawStdEncoding.EncodeToString(hasher.Sum(nil)) - - jkey, err := jwk.FromRaw(ap) - if err != nil { - log.Fatalf("failed to create symmetric key: %s\n", err) - } - jkey.Set(jwk.KeyIDKey, kid) - - buf, err := json.MarshalIndent(jkey, "", " ") - if err != nil { - fmt.Printf("failed to marshal key into JSON: %s\n", err) - return - } - fmt.Printf("JWK Format:\n%s\n", buf) - -} diff --git a/go.mod b/go.mod index 9ea89fd..e0c98f8 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,12 @@ module github.com/salrashid123/golang-jwt-tpm -go 1.20 +go 1.22 + +toolchain go1.22.2 require ( github.com/golang-jwt/jwt/v5 v5.2.1 - github.com/google/go-tpm v0.9.0 + github.com/google/go-tpm v0.9.1-0.20240514145214-58e3e47cd434 github.com/google/go-tpm-tools v0.4.0 github.com/stretchr/testify v1.9.0 ) @@ -18,7 +20,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect - golang.org/x/sys v0.8.0 // indirect + golang.org/x/sys v0.20.0 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 588f282..d167637 100644 --- a/go.sum +++ b/go.sum @@ -4,17 +4,22 @@ github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17w github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/certificate-transparency-go v1.1.2 h1:4hE0GEId6NAW28dFpC+LrRGwQX5dtmXQGDbg8+/MZOM= +github.com/google/certificate-transparency-go v1.1.2/go.mod h1:3OL+HKDqHPUfdKrHVQxO6T8nDLO0HF7LRTlkIWXaWvQ= github.com/google/go-attestation v0.4.4-0.20230613144338-a9b6eb1eb888 h1:HURgKPRPJSozDuMHpjdV+iyFVLhB6bi1JanhGgSzI1k= +github.com/google/go-attestation v0.4.4-0.20230613144338-a9b6eb1eb888/go.mod h1:xCfWZojUHwedNcs780T8cblW9XHss9XKD2s3U44FVbo= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-sev-guest v0.6.1 h1:NajHkAaLqN9/aW7bCFSUplUMtDgk2+HcN7jC2btFtk0= github.com/google/go-sev-guest v0.6.1/go.mod h1:UEi9uwoPbLdKGl1QHaq1G8pfCbQ4QP0swWX4J0k6r+Q= -github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= -github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= +github.com/google/go-tpm v0.9.1-0.20240514145214-58e3e47cd434 h1:uPadaCeI0VnloLvthGLalr0Io0IDoI1VEQ95APzVAiw= +github.com/google/go-tpm v0.9.1-0.20240514145214-58e3e47cd434/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= github.com/google/go-tpm-tools v0.4.0 h1:bYRZAUvQEmn11WTKCkTLRCCv4aTlOBgBBeqCK0ABT2A= github.com/google/go-tpm-tools v0.4.0/go.mod h1:G7PFUk8KKQzdYYGv/cpV9LB9sPT7czAAomnceugzNKQ= github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus= +github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI= github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ= github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -29,12 +34,16 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= diff --git a/tpmsigner.go b/tpmsigner.go index 76e7fa0..73e7bda 100644 --- a/tpmsigner.go +++ b/tpmsigner.go @@ -4,25 +4,32 @@ import ( "context" "crypto" "crypto/ecdsa" - "encoding/asn1" + "crypto/rsa" "errors" "fmt" "io" + "log" "math/big" jwt "github.com/golang-jwt/jwt/v5" - "github.com/google/go-tpm-tools/client" - "github.com/google/go-tpm/legacy/tpm2" + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpm2/transport" + + _ "crypto/sha256" ) // Much of this implementation is inspired templated form [gcp-jwt-go](https://github.com/someone1/gcp-jwt-go) type TPMConfig struct { TPMDevice io.ReadWriteCloser - Key *client.Key // load a key from handle + Handle tpm2.TPMHandle // load a key from handle + Session tpm2.Session // (optional) session to use, defaults to tpm2.PasswordAuth(nil) KeyID string // (optional) the TPM keyID (normally the key "Name") publicKeyFromTPM crypto.PublicKey // the public key as read from KeyHandleFile, KeyHandleNV + name tpm2.TPM2BName + EncryptionHandle tpm2.TPMHandle // (optional) handle to use for transit encryption + EncryptionPub *tpm2.TPMTPublic // (optional) public key to use for transit encryption } type tpmConfigKey struct{} @@ -50,19 +57,72 @@ type SigningMethodTPM struct { func NewTPMContext(parent context.Context, val *TPMConfig) (context.Context, error) { // first check if a TPM is even involved in the picture here since we can verify w/o a TPM - if val.TPMDevice == nil || val.Key == nil { + if val.TPMDevice == nil || val.Handle == 0 { return nil, fmt.Errorf("tpmjwt: tpm device or key not set") } - switch val.Key.PublicArea().Type { - case tpm2.AlgRSA: - // do optional validation - case tpm2.AlgECC: - // do optional validation + rwr := transport.FromReadWriter(val.TPMDevice) + + pub, err := tpm2.ReadPublic{ + ObjectHandle: tpm2.TPMHandle(val.Handle.HandleValue()), + }.Execute(rwr) + if err != nil { + return nil, fmt.Errorf("tpmjwt: error executing tpm2.ReadPublic %v", err) + } + + if val.Session == nil { + val.Session = tpm2.PasswordAuth(nil) + } + + outPub, err := pub.OutPublic.Contents() + if err != nil { + return nil, fmt.Errorf("tpmjwt: error reading public contexts %v", err) + } + + val.name = pub.Name + + var keyPub crypto.PublicKey + + switch outPub.Type { + case tpm2.TPMAlgRSA: + rsaDetail, err := outPub.Parameters.RSADetail() + if err != nil { + return nil, fmt.Errorf("tpmjwt: error reading rsa public %v", err) + } + rsaUnique, err := outPub.Unique.RSA() + if err != nil { + return nil, fmt.Errorf("tpmjwt: error reading rsa unique %v", err) + } + + rsaPub, err := tpm2.RSAPub(rsaDetail, rsaUnique) + if err != nil { + log.Fatalf("Failed to get rsa public key: %v", err) + } + keyPub = rsaPub + case tpm2.TPMAlgECC: + ecDetail, err := outPub.Parameters.ECCDetail() + if err != nil { + return nil, fmt.Errorf("tpmjwt: error reading ec details %v", err) + } + crv, err := ecDetail.CurveID.Curve() + if err != nil { + return nil, fmt.Errorf("tpmjwt: error reading ecc curve %v", err) + } + eccUnique, err := outPub.Unique.ECC() + if err != nil { + return nil, fmt.Errorf("tpmjwt: error reading ecc unique %v", err) + } + + pubKey := &ecdsa.PublicKey{ + Curve: crv, + X: big.NewInt(0).SetBytes(eccUnique.X.Buffer), + Y: big.NewInt(0).SetBytes(eccUnique.Y.Buffer), + } + keyPub = pubKey default: - return nil, fmt.Errorf("tpmjwt: unsupported Algorithm %s", val.Key.PublicArea().Type) + return nil, fmt.Errorf("tpmjwt: unsupported Algorithm %v", outPub.Type) } - val.publicKeyFromTPM = val.Key.PublicKey() + val.publicKeyFromTPM = keyPub return context.WithValue(parent, tpmConfigKey{}, val), nil } @@ -134,33 +194,139 @@ func (s *SigningMethodTPM) Sign(signingString string, key interface{}) ([]byte, if !ok { return nil, errMissingConfig } - tsig, err := config.Key.SignData([]byte(signingString)) - if err != nil { - return nil, fmt.Errorf("tpmjwt: can't Sign %s: %v", config.TPMDevice, err) + var tsig []byte + + h := s.hasher.New() + _, err := h.Write([]byte(signingString)) + if !ok { + return nil, err } + digest := h.Sum(nil) - if config.Key.PublicArea().Type == tpm2.AlgECC { - // go-tpm-tools formats ECC signatures as asn1 but JWT expects raw so convert - // the asn1 back - epub, ok := config.Key.PublicKey().(*ecdsa.PublicKey) - if !ok { - return nil, fmt.Errorf("tpmjwt: error converting ECC keytype %v", err) - } - curveBits := epub.Params().BitSize - keyBytes := curveBits / 8 - if curveBits%8 > 0 { - keyBytes += 1 + rwr := transport.FromReadWriter(config.TPMDevice) + + var sess tpm2.Session + // check if we should use parameter encryption...if so, just use the EK for now + if config.EncryptionHandle != 0 && config.EncryptionPub != nil { + sess = tpm2.HMAC(tpm2.TPMAlgSHA256, 16, tpm2.AESEncryption(128, tpm2.EncryptIn), tpm2.Salted(config.EncryptionHandle, *config.EncryptionPub)) + } else { + sess = tpm2.HMAC(tpm2.TPMAlgSHA256, 16, tpm2.AESEncryption(128, tpm2.EncryptIn)) + } + + switch pub := config.publicKeyFromTPM.(type) { + case *rsa.PublicKey: + if s.Alg() == "RS256" { + rspSign, err := tpm2.Sign{ + KeyHandle: tpm2.AuthHandle{ + Handle: tpm2.TPMHandle(config.Handle.HandleValue()), + Name: config.name, + Auth: config.Session, + }, + Digest: tpm2.TPM2BDigest{ + Buffer: digest[:], + }, + InScheme: tpm2.TPMTSigScheme{ + Scheme: tpm2.TPMAlgRSASSA, + Details: tpm2.NewTPMUSigScheme( + tpm2.TPMAlgRSASSA, + &tpm2.TPMSSchemeHash{ + HashAlg: tpm2.TPMAlgSHA256, + }, + ), + }, + Validation: tpm2.TPMTTKHashCheck{ + Tag: tpm2.TPMSTHashCheck, + }, + }.Execute(rwr, sess) + if err != nil { + return nil, fmt.Errorf("tpmjwt: can't Sign: %v", err) + } + + rsig, err := rspSign.Signature.Signature.RSASSA() + if err != nil { + return nil, fmt.Errorf("tpmjwt: error getting rsa signature: %v", err) + } + tsig = rsig.Sig.Buffer + + } else if s.Alg() == "PS256" { + rspSign, err := tpm2.Sign{ + KeyHandle: tpm2.AuthHandle{ + Handle: tpm2.TPMHandle(config.Handle.HandleValue()), + Name: config.name, + Auth: config.Session, + }, + Digest: tpm2.TPM2BDigest{ + Buffer: digest[:], + }, + InScheme: tpm2.TPMTSigScheme{ + Scheme: tpm2.TPMAlgRSAPSS, + Details: tpm2.NewTPMUSigScheme( + tpm2.TPMAlgRSAPSS, + &tpm2.TPMSSchemeHash{ + HashAlg: tpm2.TPMAlgSHA256, + }, + ), + }, + Validation: tpm2.TPMTTKHashCheck{ + Tag: tpm2.TPMSTHashCheck, + }, + }.Execute(rwr, sess) + if err != nil { + return nil, fmt.Errorf("tpmjwt: can't Sign pss %v", err) + } + + rsig, err := rspSign.Signature.Signature.RSAPSS() + if err != nil { + return nil, fmt.Errorf("tpmjwt: error getting rsa pss signature %v", err) + } + + tsig = rsig.Sig.Buffer + } else { + return nil, fmt.Errorf("tpmjwt: unsupported rsa algorithm %s", s.Alg()) } - out := make([]byte, 2*keyBytes) - var sigStruct struct{ R, S *big.Int } - _, err := asn1.Unmarshal(tsig, &sigStruct) - if err != nil { - return nil, fmt.Errorf("tpmjwt: can't unmarshall ecc struct %v", err) + + case *ecdsa.PublicKey: + if s.Alg() == "ES256" { + rspSign, err := tpm2.Sign{ + KeyHandle: tpm2.AuthHandle{ + Handle: tpm2.TPMHandle(config.Handle.HandleValue()), + Name: config.name, + Auth: config.Session, + }, + Digest: tpm2.TPM2BDigest{ + Buffer: digest[:], + }, + InScheme: tpm2.TPMTSigScheme{ + Scheme: tpm2.TPMAlgECDSA, + Details: tpm2.NewTPMUSigScheme( + tpm2.TPMAlgECDSA, + &tpm2.TPMSSchemeHash{ + HashAlg: tpm2.TPMAlgSHA256, + }, + ), + }, + Validation: tpm2.TPMTTKHashCheck{ + Tag: tpm2.TPMSTHashCheck, + }, + }.Execute(rwr, tpm2.HMAC(tpm2.TPMAlgSHA256, 16, tpm2.AESEncryption(128, tpm2.EncryptIn))) + if err != nil { + return nil, fmt.Errorf("tpmjwt: can't Sign ecc: %v", err) + } + + rsig, err := rspSign.Signature.Signature.ECDSA() + if err != nil { + return nil, fmt.Errorf("tpmjwt: getting rsa ecc signature: %v", err) + } + out := append(rsig.SignatureR.Buffer, rsig.SignatureS.Buffer...) + return out, nil + + } else { + return nil, fmt.Errorf("tpmjwt: unsupported EC algorithm %s", s.Alg()) } - sigStruct.R.FillBytes(out[0:keyBytes]) - sigStruct.S.FillBytes(out[keyBytes:]) - return out, nil + default: + return nil, fmt.Errorf("tpmjwt: unsupported public key type %v", pub) } + return tsig, nil } diff --git a/tpmsigner_test.go b/tpmsigner_test.go index aea822e..627db14 100644 --- a/tpmsigner_test.go +++ b/tpmsigner_test.go @@ -2,78 +2,150 @@ package tpmjwt import ( "context" - "crypto/rsa" "testing" "time" - jwt "github.com/golang-jwt/jwt/v5" - "github.com/google/go-tpm-tools/client" + "github.com/golang-jwt/jwt/v5" "github.com/google/go-tpm-tools/simulator" - "github.com/google/go-tpm/legacy/tpm2" - "github.com/google/go-tpm/tpmutil" + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpm2/transport" "github.com/stretchr/testify/require" ) -// copied from https://github.com/google/go-tpm-tools/blob/v0.4.0/client/signer_test.go#L18-L24 -func templateRSASSA(hash tpm2.Algorithm) tpm2.Public { - template := client.AKTemplateRSA() - // Can't sign arbitrary data if restricted. - template.Attributes &= ^tpm2.FlagRestricted - template.RSAParameters.Sign.Hash = hash - return template -} - -func templateRSAPSS(hash tpm2.Algorithm) tpm2.Public { - template := client.AKTemplateRSA() - // Can't sign arbitrary data if restricted. - template.Attributes &= ^tpm2.FlagRestricted - template.RSAParameters.Sign.Hash = hash - template.RSAParameters.Sign.Alg = tpm2.AlgRSAPSS - return template -} - -func templateECC(hash tpm2.Algorithm) tpm2.Public { - template := client.AKTemplateECC() - // Can't sign arbitrary data if restricted. - template.Attributes &= ^tpm2.FlagRestricted - template.ECCParameters.Sign.Hash = hash - return template -} - -func TestTPMPublic(t *testing.T) { - tpmDevice, err := simulator.Get() - require.NoError(t, err) - defer tpmDevice.Close() - - createdKey, err := client.NewKey(tpmDevice, tpm2.HandleOwner, client.SRKTemplateRSA()) - require.NoError(t, err) - defer createdKey.Close() - - k, err := client.LoadCachedKey(tpmDevice, createdKey.Handle(), nil) - require.NoError(t, err) - defer k.Close() +var ( + rsaTemplate = tpm2.TPMTPublic{ + Type: tpm2.TPMAlgRSA, + NameAlg: tpm2.TPMAlgSHA256, + ObjectAttributes: tpm2.TPMAObject{ + SignEncrypt: true, + FixedTPM: true, + FixedParent: true, + SensitiveDataOrigin: true, + UserWithAuth: true, + }, + AuthPolicy: tpm2.TPM2BDigest{}, + Parameters: tpm2.NewTPMUPublicParms( + tpm2.TPMAlgRSA, + &tpm2.TPMSRSAParms{ + Scheme: tpm2.TPMTRSAScheme{ + Scheme: tpm2.TPMAlgRSASSA, + Details: tpm2.NewTPMUAsymScheme( + tpm2.TPMAlgRSASSA, + &tpm2.TPMSSigSchemeRSASSA{ + HashAlg: tpm2.TPMAlgSHA256, + }, + ), + }, + KeyBits: 2048, + }, + ), + Unique: tpm2.NewTPMUPublicID( + tpm2.TPMAlgRSA, + &tpm2.TPM2BPublicKeyRSA{ + Buffer: make([]byte, 256), + }, + ), + } - k.PublicKey() - SigningMethodTPMRS256.Override() + rsaPSSTemplate = tpm2.TPMTPublic{ + Type: tpm2.TPMAlgRSA, + NameAlg: tpm2.TPMAlgSHA256, + ObjectAttributes: tpm2.TPMAObject{ + SignEncrypt: true, + FixedTPM: true, + FixedParent: true, + SensitiveDataOrigin: true, + UserWithAuth: true, + }, + AuthPolicy: tpm2.TPM2BDigest{}, + Parameters: tpm2.NewTPMUPublicParms( + tpm2.TPMAlgRSA, + &tpm2.TPMSRSAParms{ + Scheme: tpm2.TPMTRSAScheme{ + Scheme: tpm2.TPMAlgRSAPSS, + Details: tpm2.NewTPMUAsymScheme( + tpm2.TPMAlgRSAPSS, + &tpm2.TPMSSigSchemeRSAPSS{ + HashAlg: tpm2.TPMAlgSHA256, + }, + ), + }, + KeyBits: 2048, + }, + ), + Unique: tpm2.NewTPMUPublicID( + tpm2.TPMAlgRSA, + &tpm2.TPM2BPublicKeyRSA{ + Buffer: make([]byte, 256), + }, + ), + } - pubKey, ok := k.PublicKey().(*rsa.PublicKey) - require.True(t, ok) - require.Equal(t, pubKey, k.PublicKey()) - require.Equal(t, 2048, pubKey.Size()*8) -} + eccTemplate = tpm2.TPMTPublic{ + Type: tpm2.TPMAlgECC, + NameAlg: tpm2.TPMAlgSHA256, + ObjectAttributes: tpm2.TPMAObject{ + SignEncrypt: true, + FixedTPM: true, + FixedParent: true, + SensitiveDataOrigin: true, + UserWithAuth: true, + }, + AuthPolicy: tpm2.TPM2BDigest{}, + Parameters: tpm2.NewTPMUPublicParms( + tpm2.TPMAlgECC, + &tpm2.TPMSECCParms{ + CurveID: tpm2.TPMECCNistP256, + }, + ), + Unique: tpm2.NewTPMUPublicID( + tpm2.TPMAlgECC, + &tpm2.TPMSECCPoint{ + X: tpm2.TPM2BECCParameter{ + Buffer: make([]byte, 32), + }, + Y: tpm2.TPM2BECCParameter{ + Buffer: make([]byte, 32), + }, + }, + ), + } +) func TestTPMRSA(t *testing.T) { tpmDevice, err := simulator.Get() require.NoError(t, err) defer tpmDevice.Close() - createdKey, err := client.NewKey(tpmDevice, tpm2.HandleOwner, templateRSASSA(tpm2.AlgSHA256)) - require.NoError(t, err) - defer createdKey.Close() - - k, err := client.LoadCachedKey(tpmDevice, createdKey.Handle(), nil) - require.NoError(t, err) - defer k.Close() + rwr := transport.FromReadWriter(tpmDevice) + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) + require.NoError(t, err) + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + rsaKeyResponse, err := tpm2.CreateLoaded{ + ParentHandle: tpm2.AuthHandle{ + Handle: primaryKey.ObjectHandle, + Name: primaryKey.Name, + Auth: tpm2.PasswordAuth(nil), + }, + InPublic: tpm2.New2BTemplate(&rsaTemplate), + }.Execute(rwr) + require.NoError(t, err) + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: rsaKeyResponse.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() SigningMethodTPMRS256.Override() @@ -86,7 +158,8 @@ func TestTPMRSA(t *testing.T) { config := &TPMConfig{ TPMDevice: tpmDevice, - Key: k, + Handle: tpm2.TPMHandle(rsaKeyResponse.ObjectHandle), + Session: tpm2.PasswordAuth(nil), } keyctx, err := NewTPMContext(context.Background(), config) require.NoError(t, err) @@ -109,13 +182,35 @@ func TestTPMRSAFail(t *testing.T) { require.NoError(t, err) defer tpmDevice.Close() - createdKey, err := client.NewKey(tpmDevice, tpm2.HandleOwner, templateRSASSA(tpm2.AlgSHA256)) - require.NoError(t, err) - defer createdKey.Close() - - k, err := client.LoadCachedKey(tpmDevice, createdKey.Handle(), nil) - require.NoError(t, err) - defer k.Close() + rwr := transport.FromReadWriter(tpmDevice) + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) + require.NoError(t, err) + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + rsaKeyResponse, err := tpm2.CreateLoaded{ + ParentHandle: tpm2.AuthHandle{ + Handle: primaryKey.ObjectHandle, + Name: primaryKey.Name, + Auth: tpm2.PasswordAuth(nil), + }, + InPublic: tpm2.New2BTemplate(&rsaTemplate), + }.Execute(rwr) + require.NoError(t, err) + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: rsaKeyResponse.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() SigningMethodTPMRS256.Override() @@ -128,7 +223,8 @@ func TestTPMRSAFail(t *testing.T) { config := &TPMConfig{ TPMDevice: tpmDevice, - Key: k, + Handle: tpm2.TPMHandle(rsaKeyResponse.ObjectHandle), + Session: tpm2.PasswordAuth(nil), } keyctx, err := NewTPMContext(context.Background(), config) require.NoError(t, err) @@ -136,17 +232,27 @@ func TestTPMRSAFail(t *testing.T) { tokenString, err := token.SignedString(keyctx) require.NoError(t, err) - newcreatedKey, err := client.NewKey(tpmDevice, tpm2.HandleOwner, templateRSASSA(tpm2.AlgSHA256)) + rsaKeyResponse2, err := tpm2.CreateLoaded{ + ParentHandle: tpm2.AuthHandle{ + Handle: primaryKey.ObjectHandle, + Name: primaryKey.Name, + Auth: tpm2.PasswordAuth(nil), + }, + InPublic: tpm2.New2BTemplate(&rsaTemplate), + }.Execute(rwr) require.NoError(t, err) - defer newcreatedKey.Close() - newk, err := client.LoadCachedKey(tpmDevice, newcreatedKey.Handle(), nil) - require.NoError(t, err) - defer k.Close() + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() newConfig := &TPMConfig{ TPMDevice: tpmDevice, - Key: newk, + Handle: tpm2.TPMHandle(rsaKeyResponse2.ObjectHandle), + Session: tpm2.PasswordAuth(nil), } newkeyFunc, err := TPMVerfiyKeyfunc(context.Background(), newConfig) @@ -163,14 +269,35 @@ func TestTPMClaim(t *testing.T) { require.NoError(t, err) defer tpmDevice.Close() - createdKey, err := client.NewKey(tpmDevice, tpm2.HandleOwner, templateRSASSA(tpm2.AlgSHA256)) - require.NoError(t, err) - defer createdKey.Close() - - k, err := client.LoadCachedKey(tpmDevice, createdKey.Handle(), nil) - require.NoError(t, err) - defer k.Close() - + rwr := transport.FromReadWriter(tpmDevice) + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) + require.NoError(t, err) + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + rsaKeyResponse, err := tpm2.CreateLoaded{ + ParentHandle: tpm2.AuthHandle{ + Handle: primaryKey.ObjectHandle, + Name: primaryKey.Name, + Auth: tpm2.PasswordAuth(nil), + }, + InPublic: tpm2.New2BTemplate(&rsaTemplate), + }.Execute(rwr) + require.NoError(t, err) + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: rsaKeyResponse.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() SigningMethodTPMRS256.Override() issuer := "test" @@ -182,7 +309,8 @@ func TestTPMClaim(t *testing.T) { config := &TPMConfig{ TPMDevice: tpmDevice, - Key: k, + Handle: tpm2.TPMHandle(rsaKeyResponse.ObjectHandle), + Session: tpm2.PasswordAuth(nil), } keyctx, err := NewTPMContext(context.Background(), config) require.NoError(t, err) @@ -207,13 +335,35 @@ func TestTPMRSAPSS(t *testing.T) { require.NoError(t, err) defer tpmDevice.Close() - createdKey, err := client.NewKey(tpmDevice, tpm2.HandleOwner, templateRSAPSS(tpm2.AlgSHA256)) - require.NoError(t, err) - defer createdKey.Close() - - k, err := client.LoadCachedKey(tpmDevice, createdKey.Handle(), nil) - require.NoError(t, err) - defer k.Close() + rwr := transport.FromReadWriter(tpmDevice) + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) + require.NoError(t, err) + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + rsaKeyResponse, err := tpm2.CreateLoaded{ + ParentHandle: tpm2.AuthHandle{ + Handle: primaryKey.ObjectHandle, + Name: primaryKey.Name, + Auth: tpm2.PasswordAuth(nil), + }, + InPublic: tpm2.New2BTemplate(&rsaPSSTemplate), + }.Execute(rwr) + require.NoError(t, err) + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: rsaKeyResponse.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() SigningMethodTPMPS256.Override() @@ -225,7 +375,8 @@ func TestTPMRSAPSS(t *testing.T) { config := &TPMConfig{ TPMDevice: tpmDevice, - Key: k, + Handle: tpm2.TPMHandle(rsaKeyResponse.ObjectHandle), + Session: tpm2.PasswordAuth(nil), } keyctx, err := NewTPMContext(context.Background(), config) require.NoError(t, err) @@ -248,13 +399,35 @@ func TestTPMECC(t *testing.T) { require.NoError(t, err) defer tpmDevice.Close() - createdKey, err := client.NewKey(tpmDevice, tpm2.HandleOwner, templateECC(tpm2.AlgSHA256)) - require.NoError(t, err) - defer createdKey.Close() - - k, err := client.LoadCachedKey(tpmDevice, createdKey.Handle(), nil) - require.NoError(t, err) - defer k.Close() + rwr := transport.FromReadWriter(tpmDevice) + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) + require.NoError(t, err) + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + eccKeyResponse, err := tpm2.CreateLoaded{ + ParentHandle: tpm2.AuthHandle{ + Handle: primaryKey.ObjectHandle, + Name: primaryKey.Name, + Auth: tpm2.PasswordAuth(nil), + }, + InPublic: tpm2.New2BTemplate(&eccTemplate), + }.Execute(rwr) + require.NoError(t, err) + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: eccKeyResponse.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() SigningMethodTPMES256.Override() @@ -266,7 +439,8 @@ func TestTPMECC(t *testing.T) { config := &TPMConfig{ TPMDevice: tpmDevice, - Key: k, + Handle: tpm2.TPMHandle(eccKeyResponse.ObjectHandle), + Session: tpm2.PasswordAuth(nil), } keyctx, err := NewTPMContext(context.Background(), config) require.NoError(t, err) @@ -284,26 +458,123 @@ func TestTPMECC(t *testing.T) { require.True(t, vtoken.Valid) } -func TestTPMPolicy(t *testing.T) { +func TestTPMPasswordPolicy(t *testing.T) { tpmDevice, err := simulator.Get() require.NoError(t, err) defer tpmDevice.Close() - s, err := client.NewPCRSession(tpmDevice, tpm2.PCRSelection{tpm2.AlgSHA256, []int{0}}) + rwr := transport.FromReadWriter(tpmDevice) + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) + require.NoError(t, err) + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + keyPassword := []byte("pass1") + + rsaKeyResponse, err := tpm2.CreateLoaded{ + ParentHandle: tpm2.AuthHandle{ + Handle: primaryKey.ObjectHandle, + Name: primaryKey.Name, + Auth: tpm2.PasswordAuth(nil), + }, + InPublic: tpm2.New2BTemplate(&rsaTemplate), + InSensitive: tpm2.TPM2BSensitiveCreate{ + Sensitive: &tpm2.TPMSSensitiveCreate{ + UserAuth: tpm2.TPM2BAuth{ + Buffer: keyPassword, + }, + }, + }, + }.Execute(rwr) + require.NoError(t, err) + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: rsaKeyResponse.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + SigningMethodTPMRS256.Override() + + issuer := "test" + claims := &jwt.RegisteredClaims{ + ExpiresAt: &jwt.NumericDate{time.Now().Add(time.Minute * 1)}, + Issuer: issuer, + } + token := jwt.NewWithClaims(SigningMethodTPMRS256, claims) + + config := &TPMConfig{ + TPMDevice: tpmDevice, + Handle: tpm2.TPMHandle(rsaKeyResponse.ObjectHandle), + Session: tpm2.PasswordAuth(keyPassword), + } + keyctx, err := NewTPMContext(context.Background(), config) require.NoError(t, err) - ac, err := s.Auth() + + tokenString, err := token.SignedString(keyctx) require.NoError(t, err) - sessionTemplate := templateRSASSA(tpm2.AlgSHA256) - sessionTemplate.AuthPolicy = ac.Auth + // verify with TPM based publicKey + keyFunc, err := TPMVerfiyKeyfunc(context.Background(), config) + require.NoError(t, err) - createdKey, err := client.NewKey(tpmDevice, tpm2.HandleOwner, sessionTemplate) + vtoken, err := jwt.Parse(tokenString, keyFunc) require.NoError(t, err) - defer createdKey.Close() - k, err := client.LoadCachedKey(tpmDevice, createdKey.Handle(), nil) + require.True(t, vtoken.Valid) +} + +func TestTPMPasswordPolicyFail(t *testing.T) { + tpmDevice, err := simulator.Get() require.NoError(t, err) - defer k.Close() + defer tpmDevice.Close() + + rwr := transport.FromReadWriter(tpmDevice) + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) + require.NoError(t, err) + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + keyPassword := []byte("pass1") + wrongPassword := []byte("wrongpass1") + rsaKeyResponse, err := tpm2.CreateLoaded{ + ParentHandle: tpm2.AuthHandle{ + Handle: primaryKey.ObjectHandle, + Name: primaryKey.Name, + Auth: tpm2.PasswordAuth(nil), + }, + InPublic: tpm2.New2BTemplate(&rsaTemplate), + InSensitive: tpm2.TPM2BSensitiveCreate{ + Sensitive: &tpm2.TPMSSensitiveCreate{ + UserAuth: tpm2.TPM2BAuth{ + Buffer: keyPassword, + }, + }, + }, + }.Execute(rwr) + require.NoError(t, err) + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: rsaKeyResponse.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() SigningMethodTPMRS256.Override() @@ -316,57 +587,217 @@ func TestTPMPolicy(t *testing.T) { config := &TPMConfig{ TPMDevice: tpmDevice, - Key: k, + Handle: tpm2.TPMHandle(rsaKeyResponse.ObjectHandle), + Session: tpm2.PasswordAuth(wrongPassword), } keyctx, err := NewTPMContext(context.Background(), config) require.NoError(t, err) - tokenString, err := token.SignedString(keyctx) + _, err = token.SignedString(keyctx) + require.Error(t, err) +} + +func TestTPMPolicyPCR(t *testing.T) { + tpmDevice, err := simulator.Get() require.NoError(t, err) + defer tpmDevice.Close() - // verify with TPM based publicKey - keyFunc, err := TPMVerfiyKeyfunc(context.Background(), config) + rwr := transport.FromReadWriter(tpmDevice) + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) require.NoError(t, err) + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() - vtoken, err := jwt.Parse(tokenString, keyFunc) + pcr := 23 + + sess, cleanup, err := tpm2.PolicySession(rwr, tpm2.TPMAlgSHA256, 16, tpm2.Trial()) require.NoError(t, err) + defer cleanup() - require.True(t, vtoken.Valid) + _, err = tpm2.PolicyPCR{ + PolicySession: sess.Handle(), + Pcrs: tpm2.TPMLPCRSelection{ + PCRSelections: []tpm2.TPMSPCRSelection{ + { + Hash: tpm2.TPMAlgSHA256, + PCRSelect: tpm2.PCClientCompatible.PCRs(uint(pcr)), + }, + }, + }, + }.Execute(rwr) + require.NoError(t, err) + + pgd, err := tpm2.PolicyGetDigest{ + PolicySession: sess.Handle(), + }.Execute(rwr) + require.NoError(t, err) + + _, err = tpm2.FlushContext{FlushHandle: sess.Handle()}.Execute(rwr) + require.NoError(t, err) + + pcrPolicyDigest := pgd.PolicyDigest.Buffer + + rsaTemplate.AuthPolicy.Buffer = pcrPolicyDigest + + rsaKeyResponse, err := tpm2.CreateLoaded{ + ParentHandle: tpm2.AuthHandle{ + Handle: primaryKey.ObjectHandle, + Name: primaryKey.Name, + Auth: tpm2.PasswordAuth(nil), + }, + InPublic: tpm2.New2BTemplate(&rsaTemplate), + }.Execute(rwr) + require.NoError(t, err) + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: rsaKeyResponse.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + SigningMethodTPMRS256.Override() + + issuer := "test" + claims := &jwt.RegisteredClaims{ + ExpiresAt: &jwt.NumericDate{time.Now().Add(time.Minute * 1)}, + Issuer: issuer, + } + token := jwt.NewWithClaims(SigningMethodTPMRS256, claims) + + config := &TPMConfig{ + TPMDevice: tpmDevice, + Handle: tpm2.TPMHandle(rsaKeyResponse.ObjectHandle), + Session: sess, + } + keyctx, err := NewTPMContext(context.Background(), config) + require.NoError(t, err) + + _, err = token.SignedString(keyctx) + require.Error(t, err) } -func TestTPMSignPolicyFail(t *testing.T) { +func TestTPMPolicyPCRFail(t *testing.T) { tpmDevice, err := simulator.Get() require.NoError(t, err) defer tpmDevice.Close() + rwr := transport.FromReadWriter(tpmDevice) + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) + require.NoError(t, err) + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + pcr := 23 - s, err := client.NewPCRSession(tpmDevice, tpm2.PCRSelection{tpm2.AlgSHA256, []int{pcr}}) + sess, cleanup, err := tpm2.PolicySession(rwr, tpm2.TPMAlgSHA256, 16, tpm2.Trial()) require.NoError(t, err) - ac, err := s.Auth() + defer cleanup() + + _, err = tpm2.PolicyPCR{ + PolicySession: sess.Handle(), + Pcrs: tpm2.TPMLPCRSelection{ + PCRSelections: []tpm2.TPMSPCRSelection{ + { + Hash: tpm2.TPMAlgSHA256, + PCRSelect: tpm2.PCClientCompatible.PCRs(uint(pcr)), + }, + }, + }, + }.Execute(rwr) require.NoError(t, err) - sessionTemplate := templateRSASSA(tpm2.AlgSHA256) - sessionTemplate.AuthPolicy = ac.Auth + pgd, err := tpm2.PolicyGetDigest{ + PolicySession: sess.Handle(), + }.Execute(rwr) + require.NoError(t, err) - createdKey, err := client.NewKey(tpmDevice, tpm2.HandleOwner, sessionTemplate) + _, err = tpm2.FlushContext{FlushHandle: sess.Handle()}.Execute(rwr) require.NoError(t, err) - defer createdKey.Close() - pcrval, err := tpm2.ReadPCR(tpmDevice, pcr, tpm2.AlgSHA256) + pcrPolicyDigest := pgd.PolicyDigest.Buffer + + rsaTemplate.AuthPolicy.Buffer = pcrPolicyDigest + + rsaKeyResponse, err := tpm2.CreateLoaded{ + ParentHandle: tpm2.AuthHandle{ + Handle: primaryKey.ObjectHandle, + Name: primaryKey.Name, + Auth: tpm2.PasswordAuth(nil), + }, + InPublic: tpm2.New2BTemplate(&rsaTemplate), + }.Execute(rwr) require.NoError(t, err) - pcrToExtend := tpmutil.Handle(pcr) + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: rsaKeyResponse.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() - err = tpm2.PCRExtend(tpmDevice, pcrToExtend, tpm2.AlgSHA256, pcrval, "") + /// extend pcr value + + pcrReadRsp, err := tpm2.PCRRead{ + PCRSelectionIn: tpm2.TPMLPCRSelection{ + PCRSelections: []tpm2.TPMSPCRSelection{ + { + Hash: tpm2.TPMAlgSHA256, + PCRSelect: tpm2.PCClientCompatible.PCRs(uint(1)), + }, + }, + }, + }.Execute(rwr) require.NoError(t, err) - ps, err := client.NewPCRSession(tpmDevice, tpm2.PCRSelection{tpm2.AlgSHA256, []int{pcr}}) + _, err = tpm2.PCRExtend{ + PCRHandle: tpm2.AuthHandle{ + Handle: tpm2.TPMHandle(uint(pcr)), + Auth: tpm2.PasswordAuth(nil), + }, + Digests: tpm2.TPMLDigestValues{ + Digests: []tpm2.TPMTHA{ + { + HashAlg: tpm2.TPMAlgSHA256, + Digest: pcrReadRsp.PCRValues.Digests[0].Buffer, + }, + }, + }, + }.Execute(rwr) require.NoError(t, err) - loadedKey, err := client.LoadCachedKey(tpmDevice, createdKey.Handle(), ps) + /// + + newsess, newcleanup, err := tpm2.PolicySession(rwr, tpm2.TPMAlgSHA256, 16) + require.NoError(t, err) + defer newcleanup() + + _, err = tpm2.PolicyPCR{ + PolicySession: newsess.Handle(), + Pcrs: tpm2.TPMLPCRSelection{ + PCRSelections: []tpm2.TPMSPCRSelection{ + { + Hash: tpm2.TPMAlgSHA256, + PCRSelect: tpm2.PCClientCompatible.PCRs(uint(pcr)), + }, + }, + }, + }.Execute(rwr) require.NoError(t, err) - defer loadedKey.Close() SigningMethodTPMRS256.Override() @@ -379,12 +810,77 @@ func TestTPMSignPolicyFail(t *testing.T) { config := &TPMConfig{ TPMDevice: tpmDevice, - Key: loadedKey, + Handle: tpm2.TPMHandle(rsaKeyResponse.ObjectHandle), + Session: newsess, } keyctx, err := NewTPMContext(context.Background(), config) require.NoError(t, err) _, err = token.SignedString(keyctx) require.Error(t, err) +} + +func TestTPMSessionEncryption(t *testing.T) { + tpmDevice, err := simulator.Get() + require.NoError(t, err) + defer tpmDevice.Close() + + rwr := transport.FromReadWriter(tpmDevice) + primaryKey, err := tpm2.CreatePrimary{ + PrimaryHandle: tpm2.TPMRHOwner, + InPublic: tpm2.New2B(tpm2.RSASRKTemplate), + }.Execute(rwr) + require.NoError(t, err) + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: primaryKey.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + rsaKeyResponse, err := tpm2.CreateLoaded{ + ParentHandle: tpm2.AuthHandle{ + Handle: primaryKey.ObjectHandle, + Name: primaryKey.Name, + Auth: tpm2.PasswordAuth(nil), + }, + InPublic: tpm2.New2BTemplate(&rsaTemplate), + }.Execute(rwr) + require.NoError(t, err) + + defer func() { + flushContextCmd := tpm2.FlushContext{ + FlushHandle: rsaKeyResponse.ObjectHandle, + } + _, _ = flushContextCmd.Execute(rwr) + }() + + SigningMethodTPMRS256.Override() + + issuer := "test" + claims := &jwt.RegisteredClaims{ + ExpiresAt: &jwt.NumericDate{time.Now().Add(time.Minute * 1)}, + Issuer: issuer, + } + token := jwt.NewWithClaims(SigningMethodTPMRS256, claims) + + config := &TPMConfig{ + TPMDevice: tpmDevice, + Handle: tpm2.TPMHandle(rsaKeyResponse.ObjectHandle), + Session: tpm2.PasswordAuth(nil), + } + keyctx, err := NewTPMContext(context.Background(), config) + require.NoError(t, err) + + tokenString, err := token.SignedString(keyctx) + require.NoError(t, err) + + // verify with TPM based publicKey + keyFunc, err := TPMVerfiyKeyfunc(context.Background(), config) + require.NoError(t, err) + vtoken, err := jwt.Parse(tokenString, keyFunc) + require.NoError(t, err) + + require.True(t, vtoken.Valid) }