Skip to content

Commit

Permalink
govc: Cmd to enc data for VMs w TPM2 devs
Browse files Browse the repository at this point in the history
This patch introduces support for encrypting plain-text
information for VMs with TPM2 devices without the system
on which the command is run needing a TPM.

Please refer to google/go-tpm#343
for more information.
  • Loading branch information
akutz committed Sep 5, 2023
1 parent 5574c08 commit 6ff7225
Show file tree
Hide file tree
Showing 7 changed files with 603 additions and 0 deletions.
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/a8m/tree v0.0.0-20210115125333-10a5fd5b637d
github.com/dougm/pretty v0.0.0-20171025230240-2ee9d7453c02
github.com/google/go-cmp v0.5.9
github.com/google/go-tpm v0.0.0-00010101000000-000000000000
github.com/google/uuid v1.3.1
github.com/rasky/go-xdr v0.0.0-20170217172119-4930550ba2e2
github.com/stretchr/testify v1.8.4
Expand All @@ -17,6 +18,9 @@ require (
github.com/kr/pretty v0.3.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.8.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/google/go-tpm => github.com/akutz/go-tpm v0.0.0-20230904203701-1d12d24e581e
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/a8m/tree v0.0.0-20210115125333-10a5fd5b637d h1:4E8RufAN3UQ/weB6AnQ4y5miZCO0Yco8ZdGId41WuQs=
github.com/a8m/tree v0.0.0-20210115125333-10a5fd5b637d/go.mod h1:FSdwKX97koS5efgm8WevNf7XS3PqtyFkKDDXrz778cg=
github.com/akutz/go-tpm v0.0.0-20230904203701-1d12d24e581e h1:3+q0gcMmpQWxi4UI+KEmyC1OUBD4m2/O7Bcb2J/5AQ4=
github.com/akutz/go-tpm v0.0.0-20230904203701-1d12d24e581e/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand All @@ -26,6 +28,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728 h1:sH9mEk+flyDxiUa5BuPiuhDETMbzrt9A20I2wktMvRQ=
github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728/go.mod h1:x9oS4Wk2s2u4tS29nEaDLdzvuHdB19CvSGJjPgkZJNk=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
52 changes: 52 additions & 0 deletions govc/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,9 @@ but appear via `govc $cmd -h`:
- [vm.rdm.attach](#vmrdmattach)
- [vm.rdm.ls](#vmrdmls)
- [vm.register](#vmregister)
- [vm.tpm2.cert.get](#vmtpm2certget)
- [vm.tpm2.cert.ls](#vmtpm2certls)
- [vm.tpm2.seal](#vmtpm2seal)
- [vm.unregister](#vmunregister)
- [vm.upgrade](#vmupgrade)
- [vm.vnc](#vmvnc)
Expand Down Expand Up @@ -6284,6 +6287,55 @@ Options:
-template=false Mark VM as template
```

## vm.tpm2.cert.get

```
Usage: govc vm.tpm2.cert.get [OPTIONS]
Get certificate by fingerprint.
Examples:
govc vm.tpm2.cert.get -vm VM -fingerprint 41:5D:F1:AE:B9:F2:B1:22:9F:79:B7:FF:DA:55:5B:86
Options:
-fingerprint= Fingerprint of cert to get. Use vm.tpm2.ls to see available certs."
-vm= Virtual machine [GOVC_VM]
```

## vm.tpm2.cert.ls

```
Usage: govc vm.tpm2.cert.ls [OPTIONS]
List endorsement key certificates.
Examples:
govc vm.tpm2.cert.ls -vm VM
govc vm.tpm2.cert.ls -vm VM -G ecc
Options:
-G= Public key algorithm. Either "rsa", "ecc", or "ecdsa"
-vm= Virtual machine [GOVC_VM]
```

## vm.tpm2.seal

```
Usage: govc vm.tpm2.seal [OPTIONS]
Seal plain-text data to the VM's TPM2 endorsement key.
Examples:
govc vm.tpm2.seal -vm VM -in plain.txt
echo "Hello, world" | govc vm.tpm2.seal -vm VM
echo "Seal with ECC." | govc vm.tpm2.seal -vm VM -G ecc
Options:
-G=rsa Public key algorithm. Either "rsa", "ecc", or "ecdsa"
-in=- Input data. Defaults to STDIN via "-"
-vm= Virtual machine [GOVC_VM]
```

## vm.unregister

```
Expand Down
2 changes: 2 additions & 0 deletions govc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ import (
_ "github.com/vmware/govmomi/govc/vm/option"
_ "github.com/vmware/govmomi/govc/vm/rdm"
_ "github.com/vmware/govmomi/govc/vm/snapshot"
_ "github.com/vmware/govmomi/govc/vm/tpm2"
_ "github.com/vmware/govmomi/govc/vm/tpm2/cert"
_ "github.com/vmware/govmomi/govc/volume"
_ "github.com/vmware/govmomi/govc/volume/snapshot"
_ "github.com/vmware/govmomi/govc/vsan"
Expand Down
162 changes: 162 additions & 0 deletions govc/vm/tpm2/cert/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
Copyright (c) 2023-2023 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cert

import (
"bytes"
"context"
"crypto/md5"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"flag"
"fmt"
"io"

"github.com/vmware/govmomi/govc/cli"
"github.com/vmware/govmomi/govc/flags"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)

type get struct {
*flags.VirtualMachineFlag
*flags.OutputFlag

fingerprint string
}

func init() {
cli.Register("vm.tpm2.cert.get", &get{})
}

func (cmd *get) Register(ctx context.Context, f *flag.FlagSet) {

cmd.VirtualMachineFlag, ctx = flags.NewVirtualMachineFlag(ctx)
cmd.VirtualMachineFlag.Register(ctx, f)
cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
cmd.OutputFlag.Register(ctx, f)

f.StringVar(&cmd.fingerprint, "fingerprint", "",
`Fingerprint of cert to get. Use vm.tpm2.ls to see available certs."`)
}

func (cmd *get) Description() string {
return `Get certificate by fingerprint.
Examples:
govc vm.tpm2.cert.get -vm VM -fingerprint 41:5D:F1:AE:B9:F2:B1:22:9F:79:B7:FF:DA:55:5B:86`
}

func (cmd *get) Process(ctx context.Context) error {

if err := cmd.VirtualMachineFlag.Process(ctx); err != nil {
return err
}
if err := cmd.OutputFlag.Process(ctx); err != nil {
return err
}
return nil
}

func (cmd *get) Run(ctx context.Context, f *flag.FlagSet) error {
if cmd.fingerprint == "" {
return flag.ErrHelp
}

vm, err := cmd.VirtualMachine()
if err != nil {
return err
}

if vm == nil {
return flag.ErrHelp
}

// Get the VM's EK.
var moVM mo.VirtualMachine
if err := vm.Properties(
ctx,
vm.Reference(),
[]string{"config.hardware.device"},
&moVM); err != nil {
return err
}

devices := object.VirtualDeviceList(moVM.Config.Hardware.Device)
selectedDevices := devices.SelectByType(&types.VirtualTPM{})
if len(selectedDevices) == 0 {
return fmt.Errorf("no VirtualTPM devices found")
}
if len(selectedDevices) > 1 {
return fmt.Errorf("multiple VirtualTPM devices found")
}

vtpmDev := selectedDevices[0].(*types.VirtualTPM)

var result getResult
for i := range vtpmDev.EndorsementKeyCertificate {
// Use DecodeString as Decode complains about trailing data.
derString := string(vtpmDev.EndorsementKeyCertificate[i])
derData, err := base64.StdEncoding.DecodeString(derString)
if err != nil {
return err
}
cert, err := x509.ParseCertificate(derData)
if err != nil {
return err
}

fingerprint := md5.Sum(cert.Raw)
var fingerprintBuf bytes.Buffer
for i, f := range fingerprint {
if i > 0 {
fmt.Fprintf(&fingerprintBuf, ":")
}
fmt.Fprintf(&fingerprintBuf, "%02X", f)
}

if cmd.fingerprint == fingerprintBuf.String() {
var pemBuf bytes.Buffer
block := &pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Raw,
}
if err := pem.Encode(&pemBuf, block); err != nil {
return err
}
result.PubCertPEM = pemBuf.Bytes()
break
}
}

if len(result.PubCertPEM) == 0 {
return fmt.Errorf("fingerprint %s not found", cmd.fingerprint)
}

return cmd.WriteResult(result)
}

type getResult struct {
PubCertPEM []byte `json:"pubCertPEM"`
}

func (r getResult) Write(w io.Writer) error {
_, err := fmt.Fprint(w, string(r.PubCertPEM))
return err
}
Loading

0 comments on commit 6ff7225

Please sign in to comment.