gox509tpm

Cannot parse TPM2.0 public key


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)

}

Solution

  • 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..