tpmk

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 18, 2019 License: BSD-3-Clause Imports: 16 Imported by: 2

README

tmpk - TPM2 key and storage management toolkit

This project is under active development. Library interfaces and commands are likely to change.

This toolkit strives to simplify common tasks around key and certificates involving TPM2. It also provides the tools necessary to make use of keys in the module for TLS connections in Go. It does not attempt to provide a feature-rich interface to support all possible use-cases and features. tpmk consists of a Go library and a tool with a simple interface. It currently provides:

  • Generating RSA/SHA256 primary keys in the TPM and exporting the public key
  • Generating x509 certificates and signing with a (file) CA
  • Writing of arbirary data to NV storage - intended to be used to store certificates
  • Creating SSH certificates for keys in the TPM

A range of features and options are not available at this point, but may be implemented in the future. Suggestions and contributions are welcome.

  • Generating ECC keys
  • PCRs
  • TPM policies
  • Only supports the owner hierarchy

Tool

The tool is provided for convenience and should allow the use of the most common features. Some of the operations offered by its sub-commands, like x509 can be performed with more feature-rich tools such as openssl. The tool is able to operate on a TPM 2.0 simulator (use option -d sim).

Installation
go get -u github.com/folbricht/tpmk/cmd/tpmk
Sub-Commands
  • key Groups commands that operate on keys in the TPM

    • generate Generates a new primary key and makes it persistent
    • read Reads the public key
    • rm Removes a persistent key
    • ls List persistent keys
  • nv Contains commands to operate on non-volatile indexes in the TMP

    • write Write arbitrary data into an index
    • read Read data from an index
    • rm Delete data in an index
    • ls List indexes
  • x509 Offers commands to generate and sign certificates

    • generate Generate a new certificate for a public key and sign it
  • ssh Commands to operate on SSH certificates

    • certificate Create and sign an SSH certificate
    • pub Convert a PKCS1 key to OpenSSH format

Use-cases / Examples

RSA key generation and certificate storage in NV

In this example, the goal is to have an RSA key generated in the TPM, and have a signed certificate for the key stored in NV in the TPM. This allows a machine to retain a signed key+certificate without relying on disk storage. While the generated keys doesn't leave the module, the CA to sign it is expected to be available as files at time of signing.

Generate the key in the TPM and write the public key (in PEM format) to disk.

tpmk key generate 0x81000000 pub.pem

Build a certificate using the public key and signing it with a (file) CA and writing it to disk in PEM format. Options to set the common name, SAN properties, expiry, and others are available.

tpmk x509 generate -c ca.crt -k ca.key -f der pub.pem cert.pem

Store the (signed) certificate in the TPM in an NV storage index. This command stores the PEM cert which is not recommended since the amount of data that can be stored in a single NV index is limited. A large PEM may not fit. It'd be better to store the cert in DER format instead (available with --out-format=der option above).

tpmk nv -d sim write 0x1500000 cert.pem

The following does the same, but passes the data through STDIN/STDOUT without storing anything on disk. It also stores the certificate in DER format instead of PEM to ensure it fits into the NV index.

tpmk key generate 0x81000000 - | tpmk x509 generate -c ca.crt -k ca.key --out-format=der - -| tpmk nv write 0x1500000 -
Establishing a mutual TLS connection using a TPM key

Here the goal is to use a key from the TPM to establish a mutual TLS connection with a server. The key is assumed to have been generated already (see prior example). While not strictly neccessary, the signed x509 is kept in NV as well.

Start off with some setup. Defining the handle/index that hold the key and certificate and open the TPM.

const (
  keyHandle = 0x81000000
  nvHandle  = 0x1500000
  password  = ""
)

// Open the TPM
dev, err := tpmk.OpenDevice("/dev/tpmrm0")
if err!=nil{
    panic(err)
}
defer dev.Close()

The next step is to create a crypto.PrivateKey that can be used in the TLS negotiation, as well as loading the x509 certificate from NV.

// Use the private key in the TPM
private, err := tpmk.NewRSAPrivateKey(dev, keyHandle, password)
if err != nil {
  panic(err)
}

// Read the certificate (DER format) from NV
certDER, err := tpmk.NVRead(dev, nvHandle, password)
if err != nil {
  panic(err)
}

With the client certificate and key it's now possible to build the TLS client config. The CA certificate is also required to trust the server.

// Build the client certificate for the mutual TLS connection
clientCrt := tls.Certificate{
  Certificate: [][]byte{certDER},
  PrivateKey:  private,
}

// Load the CA certificate
caCrt, err := tpmk.LoadX509CertificateFile("ca.crt")
if err != nil {
  panic(err)
}

// Build the client TLS config
root := x509.NewCertPool()
root.AddCert(caCrt)
clientCfg := &tls.Config{
  Certificates: []tls.Certificate{clientCrt},
  RootCAs:      root,
}

Using the TLS client config, the client can now connect to a server that expects a client certificate. The client key remains in the TPM.

// Connect to the server
conn, err := tls.Dial("tcp", "localhost:1234", clientCfg)
if err != nil {
  panic(err)
}
defer conn.Close()
conn.Write([]byte("hello"))
SSH certificate generation

This example shows how to create an SSH certificate for a key in the TPM. It also covers reading and converting a key to OpenSSH format. The key is assumed to be present in the TPM already. An earlier example shows how to generate one.

First, the public key is read from the TPM. The result is PEM encoded PKCS#1.

tpmk key read 0x81000000 pub.pem

To generate and sign an SSH certificate, an SSH CA key is required which is provided as file (ssh-ca) in this example. A key identifier, serial or certificate option can be provided. If the generated certificate is to be stored in an NV index, the -f wire option is recommended as this will produce a more compact encoding.

tpmk ssh certificate --ca-key ssh-ca --id myKey -O force-command=ls pub.pem tpm-cert.pub

If a public key conversion to OpenSSH format is needed, the pub subcommand can be used.

tpmk ssh pub pub.pem id_rsa.pub

As before, these commands can be chained together via STDOUT/STDIN by providing - in place of filenames.

Open SSH connection using public key authentication with TPM key

The following example shows how to utilize a TPM key to open an SSH connection to a server using golang.org/x/crypto/ssh as client.

First open the TPM device or simulator. The key 0x81000000 is assumed to be present and will be used for authentication.

const (
  keyHandle = 0x81000000
  password  = ""
)

dev, err := tpmk.OpenDevice("/dev/tpmrm0")
if err!=nil{
    panic(err)
}
defer dev.Close()

Read the public key from the TPM and use it to create crypto.Signer which can be used to build an ssh.Signer used in public key authentication.

// Use the private key in the TPM
private, err := tpmk.NewRSAPrivateKey(dev, keyHandle, password)
if err != nil {
  panic(err)
}
// Create an ssh.Signer to be used for key authentication
signer, err := ssh.NewSignerFromSigner(private)
if err != nil {
  panic(err)
}
// Build the client configuration
config := &ssh.ClientConfig{
  User: "username",
  Auth: []ssh.AuthMethod{
    ssh.PublicKeys(signer),
  },
  HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}

Open the SSH client connection. The public key in OpenSSH format needs to be setup on the server for authentication to succeed.

client, err := ssh.Dial("tcp", "hostname:22", config)
if err != nil {
  panic(err)
}

session, err := client.NewSession()
if err != nil {
  panic(err)
}
defer session.Close()

b, err := session.Output("/usr/bin/whoami")
if err != nil {
  panic(err)
}
fmt.Println(string(b))

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CertToPEM

func CertToPEM(der []byte) []byte

CertToPEM encodes an x509 certificate from DER format to PEM

func DeleteKey

func DeleteKey(dev io.ReadWriteCloser, handle tpmutil.Handle, password string) error

DeleteKey removes a persistent key.

func GenRSAPrimaryKey

func GenRSAPrimaryKey(dev io.ReadWriteCloser, handle tpmutil.Handle, parentPW, ownerPW string, attr tpm2.KeyProp) (crypto.PublicKey, error)

GenRSAPrimaryKey generates a primary RSA key and makes it persistent under the given handle.

func GetHandles

func GetHandles(dev io.ReadWriteCloser, start tpm2.TPMProp) ([]tpmutil.Handle, error)

GetHandles returns a list of all handles of a type determined by by the starting position.

func KeyList

func KeyList(dev io.ReadWriteCloser) ([]tpmutil.Handle, error)

KeyList returns a list of persistent key handles.

func LoadExternal

func LoadExternal(dev io.ReadWriteCloser, handle tpmutil.Handle, pk crypto.PrivateKey, password string, attr tpm2.KeyProp) (tpmutil.Handle, error)

LoadExternal loads an existing key-pair into the TPM and returns the key handle. The key is loaded / into the Null hierarchy and not persistent.

func LoadKeyPair

func LoadKeyPair(crtFilePEM, keyFilePEM string) (*x509.Certificate, crypto.PrivateKey, error)

LoadKeyPair reads and parses a key and certificate file in PEM format.

func LoadRSAKeyFile

func LoadRSAKeyFile(keyFilePEM string) (crypto.PrivateKey, error)

LoadRSAKeyFile reads a private RSA key in PEM format from a file.

func LoadX509CertificateFile

func LoadX509CertificateFile(crtFilePEM string) (*x509.Certificate, error)

LoadX509CertificateFile reads a certificate in PEM format from a file.

func MarshalSSHPublic

func MarshalSSHPublic(k ssh.PublicKey, id string) []byte

MarshalSSHPublic encodes a certificate or public key into a format that can be used by OpenSSH.

func NVDelete

func NVDelete(dev io.ReadWriteCloser, index tpmutil.Handle, password string) error

NVDelete undefines the space used by an NV index, effectively deleting the data in it.

func NVList

func NVList(dev io.ReadWriteCloser) ([]tpmutil.Handle, error)

NVList returns a list of handles for defined NV indexes.

func NVRead

func NVRead(dev io.ReadWriteCloser, index tpmutil.Handle, password string) ([]byte, error)

NVRead returns the raw data stored in an NV index.

func NVWrite

func NVWrite(dev io.ReadWriteCloser, index tpmutil.Handle, b []byte, password string, attr tpm2.NVAttr) error

NVWrite reserves space in an NV index and writes to it starting at offset 0. It automatically determines the max buffer size prior to writing blocks to the index.

func OpenDevice

func OpenDevice(device string) (io.ReadWriteCloser, error)

OpenDevice opens a TPM2. If device is 'sim', it'll connect to a simulator. The caller is responsible for calling Close().

func PEMToPrivKey

func PEMToPrivKey(b []byte) (crypto.PrivateKey, error)

PEMToPrivKey decodes a Private key in PCKS1 PEM format.

func PEMToPubKey

func PEMToPubKey(b []byte) (crypto.PublicKey, error)

PEMToPubKey decodes a public key in PCKS1 PEM format.

func PubKeyToPEM

func PubKeyToPEM(pub crypto.PublicKey) ([]byte, error)

PubKeyToPEM encodes a public key in PEM format.

func ReadPublicKey

func ReadPublicKey(dev io.ReadWriteCloser, handle tpmutil.Handle) (tpm2.Public, crypto.PublicKey, error)

ReadPublicKey reads the public part of a key stored in the TPM. It returns the whole public part as well as the public key from it

Types

type RSAPrivateKey

type RSAPrivateKey struct {
	// contains filtered or unexported fields
}

RSAPrivateKey represents an RSA key in a TPM and implements the crypto.PrivateKey interface which allows it to be used in TLS connections.

func NewRSAPrivateKey

func NewRSAPrivateKey(dev io.ReadWriteCloser, handle tpmutil.Handle, password string) (RSAPrivateKey, error)

NewRSAPrivateKey initializes crypto.PrivateKey with a private key that is held in the TPM.

func (RSAPrivateKey) Decrypt

func (k RSAPrivateKey) Decrypt(rand io.Reader, msg []byte, opts crypto.DecrypterOpts) ([]byte, error)

Decrypt decrypts ciphertext with the key in the TPM. If opts is nil or of type *PKCS1v15DecryptOptions then PKCS#1 v1.5 decryption is performed. Otherwise opts must have type *OAEPOptions and OAEP decryption is performed. tpm2.FlagDecrypt needs to be set and tpm2.FlagRestricted clear in the key properties. Implements crypto.Decrypter. Note that using OAEP with a label requires a null-terminated string.

func (RSAPrivateKey) Public

func (k RSAPrivateKey) Public() crypto.PublicKey

Public returns the public part of the key.

func (RSAPrivateKey) Sign

func (k RSAPrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error)

Sign digests via a key in the TPM. Implements crypto.Signer. If opts are *rsa.PSSOptions, the PSS signature algorithm is used, PKCS#1 1.5 otherwise. To use this function, tpm2.FlagSign needs to be set on the key, and tpm2.FlagRestricted needs to be clear.

type Simulator

type Simulator struct {
	*mssim.Conn
}

Simulator is a wrapper around a simulator connection that ensures startup and shutdown are called on open/close. This is only necessary with simulators. If shutdown isn't called before disconnecting, the lockout counter in the simulator is incremented.

func OpenSim

func OpenSim() (Simulator, error)

OpenSim opens a connection to a local TPM2 simulator via TCP and initalizes it by calling Startup.

func (Simulator) Close

func (s Simulator) Close() error

Close calls Shutdown() on the simulator before disconnecting to ensure the lockout counter doesn't get incremented.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL