I am unsuccessfully trying to use Go to parse a TPM2.0 public key. The error that I receive seems to indicate that the parsing fail due to some sort of 'structure error'. The full error is shown below. I also have created a minimal reproducible example and posted it below. How can I resolve this parsing issue?
Error:
{"time":"2024-09-06T18:11:36.5146545Z","level":"INFO","msg":"AK Public PEM is: ","!BADKEY":"-----BEGIN TPM2 PUBLIC KEY-----\nBIIBGAABAAsABSRyAAAAEAAUAAsIAAAAAAABAJqe9l3iktbQ5bPENbFbNvOeg3up\nNKvZZ+EcdUPltkibbs2N/DBfTLaOoGmkByHn16F0Sim8yV14cMQ75CBUvNCq/yFE\nVPwJCCrM3kRo7Z+yPvftgtctKHRCKi9VouDaRfEIwIOMlYFtksyoXaS4Box29WiU\n52Dm9O5AUChsgkeJB+e8DVZd2oZX4s7TGaGif1b4mYtKcTJqVzv55S05NW4TPoTc\nXJbhdTjDqiNbaL5BUklyevYqj8XF4GzbmdGohF9wIYcuoNJo03ZcntScteFynReL\n3BFVCZCNlvNoNN1QY6xKdKevDdwVBgfXWrOGGlSW4PpmJTH1tMeXx3xwlOM=\n-----END TPM2 PUBLIC KEY-----\n"}
{"time":"2024-09-06T18:11:36.5153675Z","level":"INFO","msg":"Block type is: ","!BADKEY":"TPM2 PUBLIC KEY"}
{"time":"2024-09-06T18:11:36.517942375Z","level":"INFO","msg":"asn1: structure error: tags don't match (16 vs {class:0 tag:4 length:280 isCompound:false}) {optional:false explicit:false application:false private:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false} publicKeyInfo @4"}
Example:
package main
import (
"crypto/x509"
"encoding/pem"
"fmt"
"github.com/google/go-attestation/attest"
"log/slog"
"os"
)
// Global variables
var (
port string
tpmPath string
tpm *attest.TPM
logger *slog.Logger
knownEKs map[string][]byte
currentChallenge []byte
)
// TPM2PublicKey represents the structure of a TPM2 public key
type TPM2PublicKey struct {
Type uint16
NameAlg uint16
Attributes uint32
AuthPolicy []byte
Parameters TPM2PublicKeyParameters
Unique TPM2PublicKeyUnique
}
type TPM2PublicKeyParameters struct {
SymmetricAlgorithm uint16
Scheme uint16
KeyBits uint16
Exponent uint32
}
type TPM2PublicKeyUnique struct {
RSA []byte
}
func main() {
// Initialize structured logger
logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
akPubPEM := "-----BEGIN TPM2 PUBLIC KEY-----\nBIIBGAABAAsABSRyAAAAEAAUAAsIAAAAAAABAJqe9l3iktbQ5bPENbFbNvOeg3up\nNKvZZ+EcdUPltkibbs2N/DBfTLaOoGmkByHn16F0Sim8yV14cMQ75CBUvNCq/yFE\nVPwJCCrM3kRo7Z+yPvftgtctKHRCKi9VouDaRfEIwIOMlYFtksyoXaS4Box29WiU\n52Dm9O5AUChsgkeJB+e8DVZd2oZX4s7TGaGif1b4mYtKcTJqVzv55S05NW4TPoTc\nXJbhdTjDqiNbaL5BUklyevYqj8XF4GzbmdGohF9wIYcuoNJo03ZcntScteFynReL\n3BFVCZCNlvNoNN1QY6xKdKevDdwVBgfXWrOGGlSW4PpmJTH1tMeXx3xwlOM=\n-----END TPM2 PUBLIC KEY-----\n"
logger.Info("AK Public PEM is: ", akPubPEM)
block, _ := pem.Decode([]byte(akPubPEM))
logger.Info("Block type is: ", block.Type)
if block == nil || block.Type != "TPM2 PUBLIC KEY" {
logger.Info("failed to decode PEM block containing public key")
os.Exit(1)
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
logger.Info(err.Error())
os.Exit(1)
}
fmt.Printf("Parsed public key: %v\n", pub)
// Unmarshal ASN.1 structure
// var tpm2Pub TPM2PublicKey
// _, err := asn1.Unmarshal(block.Bytes, &tpm2Pub)
// if err != nil {
// logger.Info("ERROR: Failed to unmarshal: ", err)
// os.Exit(1)
// }
// // Convert TPM2 public key to RSA public key
// if tpm2.Algorithm(tpm2Pub.Type) != tpm2.AlgRSA {
// os.Exit(1)
// }
os.Exit(0)
}
You have your key with a base64 payload:
-----BEGIN TPM2 PUBLIC KEY-----
BIIBGAABAAsABSRyAAAAEAAUAAsIAAAAAAABAJqe9l3iktbQ5bPENbFbNvOeg3up
NKvZZ+EcdUPltkibbs2N/DBfTLaOoGmkByHn16F0Sim8yV14cMQ75CBUvNCq/yFE
VPwJCCrM3kRo7Z+yPvftgtctKHRCKi9VouDaRfEIwIOMlYFtksyoXaS4Box29WiU
52Dm9O5AUChsgkeJB+e8DVZd2oZX4s7TGaGif1b4mYtKcTJqVzv55S05NW4TPoTc
XJbhdTjDqiNbaL5BUklyevYqj8XF4GzbmdGohF9wIYcuoNJo03ZcntScteFynReL
3BFVCZCNlvNoNN1QY6xKdKevDdwVBgfXWrOGGlSW4PpmJTH1tMeXx3xwlOM=
-----END TPM2 PUBLIC KEY-----
If you base64-decode the payload, you get the following binary blob (here as hex string):
048201180001000b00052472000000100014000b08000000000001009a9e
f65de292d6d0e5b3c435b15b36f39e837ba934abd967e11c7543e5b6489b
6ecd8dfc305f4cb68ea069a40721e7d7a1744a29bcc95d7870c43be42054
bcd0aaff214454fc09082accde4468ed9fb23ef7ed82d72d2874422a2f55
a2e0da45f108c0838c95816d92cca85da4b8068c76f56894e760e6f4ee40
50286c82478907e7bc0d565dda8657e2ced319a1a27f56f8998b4a71326a
573bf9e52d39356e133e84dc5c96e17538c3aa235b68be415249727af62a
8fc5c5e06cdb99d1a8845f7021872ea0d268d3765c9ed49cb5e1729d178b
dc115509908d96f36834dd5063ac4a74a7af0ddc150607d75ab3861a5496
e0fa662531f5b4c797c77c7094e3
This is ASN.1 DER. So we DER-decode this. The following is breaks it down. You see, this is just a DER octet string:
04 # this is an ASN.1 tag for an OCTET STRING.
820118 # length field. 0x82: length is encoded in two bytes, length: 0x0118 = 280 bytes
<octet string payload>
DER octet string payload:
0001000b00052472000000100014000b08000000000001009a9e
f65de292d6d0e5b3c435b15b36f39e837ba934abd967e11c7543e5b6489b
6ecd8dfc305f4cb68ea069a40721e7d7a1744a29bcc95d7870c43be42054
bcd0aaff214454fc09082accde4468ed9fb23ef7ed82d72d2874422a2f55
a2e0da45f108c0838c95816d92cca85da4b8068c76f56894e760e6f4ee40
50286c82478907e7bc0d565dda8657e2ced319a1a27f56f8998b4a71326a
573bf9e52d39356e133e84dc5c96e17538c3aa235b68be415249727af62a
8fc5c5e06cdb99d1a8845f7021872ea0d268d3765c9ed49cb5e1729d178b
dc115509908d96f36834dd5063ac4a74a7af0ddc150607d75ab3861a5496
e0fa662531f5b4c797c77c7094e3
The payload is a TPM TPMT_PUBLIC
structure (which you called TPM2PublicKey
). Let's decode that:
TPMT_PUBLIC .
TPMI_ALG_PUBLIC | .type 0001 TPMI_ALG_PUBLIC.RSA
TPMI_ALG_HASH | .nameAlg 000b TPMI_ALG_HASH.SHA256
TPMA_OBJECT | .objectAttributes 00052472 TPMA_OBJECT.fixedTPM | TPMA_OBJECT.fixedParent | TPMA_OBJECT.sensitiveDataOrigin | TPMA_OBJECT.userWithAuth | TPMA_OBJECT.noDA | TPMA_OBJECT.reserved2 | TPMA_OBJECT.restricted | TPMA_OBJECT.sign_decrypt
| | .reserved ...............................0
| | .fixedTPM ..............................1.
| | .stClear .............................0..
| | .reserved0 ............................0...
| | .fixedParent ...........................1....
| | .sensitiveDataOrigin ..........................1.....
| | .userWithAuth .........................1......
| | .adminWithPolicy ........................0.......
| | .reserved1 ......................00........
| | .noDA .....................1..........
| | .encryptedDuplication ....................0...........
| | .reserved2 ................0010............
| | .restricted ...............1................
| | .decrypt ..............0.................
| | .sign_decrypt .............1..................
| | .sign ............0...................
| | .reserved3 000000000000....................
TPM2B_DIGEST | .authPolicy
UINT16 | | .size 0000 0
list[BYTE] | | .buffer
TPMU_PUBLIC_PARMS | .parameters
TPMS_RSA_PARMS | | .rsaDetail
TPMT_SYM_DEF_OBJECT | | | .symmetric
TPMI_ALG_SYM_OBJECT | | | | .algorithm 0010 TPMI_ALG_SYM_OBJECT.NULL
TPMU_SYM_KEY_BITS | | | | .keyBits
TPMU_SYM_MODE | | | | .mode
TPMU_SYM_DETAILS | | | | .details
TPMT_RSA_SCHEME | | | .scheme
TPMI_ALG_RSA_SCHEME | | | | .scheme 0014 TPMI_ALG_RSA_SCHEME.RSASSA
TPMU_ASYM_SCHEME | | | | .details
TPMS_SIG_SCHEME_RSASSA | | | | | .rsassa
TPMI_ALG_HASH | | | | | | .hashAlg 000b TPMI_ALG_HASH.SHA256
TPMI_RSA_KEY_BITS | | | .keyBits 0800 2048
UINT32 | | | .exponent 00000000 0
TPMU_PUBLIC_ID | .unique
TPM2B_PUBLIC_KEY_RSA | | .rsa
UINT16 | | | .size 0100 256
list[BYTE] | | | .buffer 9a9ef65de292d6d0e5b3c435b15b36f39e837ba934abd967e11c7543e5b6489b6ecd8dfc305f4cb68ea069a40721e7d7a1744a29bcc95d7870c43be42054bcd0aaff214454fc09082accde4468ed9fb23ef7ed82d72d2874422a2f55a2e0da45f108c0838c95816d92cca85da4b8068c76f56894e760e6f4ee4050286c82478907e7bc0d565dda8657e2ced319a1a27f56f8998b4a71326a573bf9e52d39356e133e84dc5c96e17538c3aa235b68be415249727af62a8fc5c5e06cdb99d1a8845f7021872ea0d268d3765c9ed49cb5e1729d178bdc115509908d96f36834dd5063ac4a74a7af0ddc150607d75ab3861a5496e0fa662531f5b4c797c77c7094e3 ...].......5.[6...{.4..g..uC..H.n...0_L...i..!...tJ)..]xp.;. T....!DT...*..Dh...>....-(tB*/U...E.......m...]....v.h..`...@P(l.G.....V]..W.......V...Jq2jW;..-95n.>..\..u8..#[h.ARIrz.*....l....._p!....h.v\.....r.....U.....h4.Pc.Jt........Z...T...f%1.....|p..