Skip to content

Commit

Permalink
Merge pull request #405 from vbalain/vbalain/quoteprovider
Browse files Browse the repository at this point in the history
Use TDX quote provider to attest and verify.
  • Loading branch information
jkl73 committed Dec 27, 2023
2 parents f7d91b3 + 1a97b02 commit 805756d
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 49 deletions.
101 changes: 86 additions & 15 deletions client/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
sg "github.com/google/go-sev-guest/client"
tg "github.com/google/go-tdx-guest/client"
tabi "github.com/google/go-tdx-guest/client/linuxabi"
tpb "github.com/google/go-tdx-guest/proto/tdx"
pb "github.com/google/go-tpm-tools/proto/attest"
)

Expand All @@ -19,9 +20,9 @@ const (
)

// TEEDevice is an interface to add an attestation report from a TEE technology's
// attestation driver.
// attestation driver or quote provider.
type TEEDevice interface {
// AddAttestation uses the TEE device's attestation driver to collect an
// AddAttestation uses the TEE device's attestation driver or quote provider to collect an
// attestation report, then adds it to the correct field of `attestation`.
AddAttestation(attestation *pb.Attestation, options AttestOpts) error
// Close finalizes any resources in use by the TEEDevice.
Expand Down Expand Up @@ -144,10 +145,17 @@ type SevSnpDevice struct {

// TdxDevice encapsulates the TDX attestation device to add its attestation quote
// to a pb.Attestation.
// Deprecated: TdxDevice is deprecated. It is recommended to use TdxQuoteProvider.
type TdxDevice struct {
Device tg.Device
}

// TdxQuoteProvider encapsulates the TDX attestation device to add its attestation quote
// to a pb.Attestation.
type TdxQuoteProvider struct {
QuoteProvider tg.QuoteProvider
}

// CreateSevSnpDevice opens the SEV-SNP attestation driver and wraps it with behavior
// that allows it to add an attestation report to pb.Attestation.
func CreateSevSnpDevice() (*SevSnpDevice, error) {
Expand Down Expand Up @@ -193,6 +201,8 @@ func (d *SevSnpDevice) Close() error {

// CreateTdxDevice opens the TDX attestation driver and wraps it with behavior
// that allows it to add an attestation quote to pb.Attestation.
// Deprecated: TdxDevice is deprecated, and use of CreateTdxQuoteProvider is
// recommended to create a TEEDevice.
func CreateTdxDevice() (*TdxDevice, error) {
d, err := tg.OpenDevice()
if err != nil {
Expand All @@ -206,21 +216,15 @@ func CreateTdxDevice() (*TdxDevice, error) {
// contents of opts.Nonce.
func (d *TdxDevice) AddAttestation(attestation *pb.Attestation, opts AttestOpts) error {
var tdxNonce [tabi.TdReportDataSize]byte
if len(opts.TEENonce) == 0 {
copy(tdxNonce[:], opts.Nonce)
} else if len(opts.TEENonce) != tabi.TdReportDataSize {
return fmt.Errorf("the TEENonce size is %d. Intel TDX device requires %d", len(opts.TEENonce), tabi.TdReportDataSize)
} else {
copy(tdxNonce[:], opts.TEENonce)
err := fillTdxNonce(opts, tdxNonce[:])
if err != nil {
return err
}
quote, err := tg.GetQuote(d.Device, tdxNonce)
if err != nil {
return err
}
attestation.TeeAttestation = &pb.Attestation_TdxAttestation{
TdxAttestation: quote,
}
return nil
return setTeeAttestationTdxQuote(quote, attestation)
}

// Close will free the device handle held by the TdxDevice. Calling more
Expand All @@ -234,6 +238,73 @@ func (d *TdxDevice) Close() error {
return nil
}

// CreateTdxQuoteProvider creates the TDX quote provider and wraps it with behavior
// that allows it to add an attestation quote to pb.Attestation.
func CreateTdxQuoteProvider() (*TdxQuoteProvider, error) {
qp, err := tg.GetQuoteProvider()
if err != nil {
return nil, err
}
if qp.IsSupported() != nil {
// TDX quote provider has a fallback mechanism to fetch attestation quote
// via device driver in case ConfigFS is not supported, so checking for TDX
// device availability here. Once Device interface is fully removed from
// subsequent go-tdx-guest versions, then below OpenDevice call should be
// removed as well.
d, err2 := tg.OpenDevice()
if err2 != nil {
return nil, fmt.Errorf("neither TDX device, nor quote provider is supported")
}
d.Close()
}

return &TdxQuoteProvider{QuoteProvider: qp}, nil
}

// AddAttestation will get the TDX attestation quote given opts.TEENonce
// and add them to `attestation`. If opts.TEENonce is empty, then uses
// contents of opts.Nonce.
func (qp *TdxQuoteProvider) AddAttestation(attestation *pb.Attestation, opts AttestOpts) error {
var tdxNonce [tabi.TdReportDataSize]byte
err := fillTdxNonce(opts, tdxNonce[:])
if err != nil {
return err
}
quote, err := tg.GetQuote(qp.QuoteProvider, tdxNonce)
if err != nil {
return err
}
return setTeeAttestationTdxQuote(quote, attestation)
}

// Close will free resources held by QuoteProvider.
func (qp *TdxQuoteProvider) Close() error {
return nil
}

func fillTdxNonce(opts AttestOpts, tdxNonce []byte) error {
if len(opts.TEENonce) == 0 {
copy(tdxNonce[:], opts.Nonce)
} else if len(opts.TEENonce) != tabi.TdReportDataSize {
return fmt.Errorf("the TEENonce size is %d. Intel TDX device requires %d", len(opts.TEENonce), tabi.TdReportDataSize)
} else {
copy(tdxNonce[:], opts.TEENonce)
}
return nil
}

func setTeeAttestationTdxQuote(quote any, attestation *pb.Attestation) error {
switch q := quote.(type) {
case *tpb.QuoteV4:
attestation.TeeAttestation = &pb.Attestation_TdxAttestation{
TdxAttestation: q,
}
default:
return fmt.Errorf("unsupported quote type: %T", quote)
}
return nil
}

// Does best effort to get a TEE hardware rooted attestation, but won't fail fatally
// unless the user provided a TEEDevice object.
func getTEEAttestationReport(attestation *pb.Attestation, opts AttestOpts) error {
Expand All @@ -257,11 +328,11 @@ func getTEEAttestationReport(attestation *pb.Attestation, opts AttestOpts) error
}

// Try TDX.
if device, err := CreateTdxDevice(); err == nil {
if quoteProvider, err := CreateTdxQuoteProvider(); err == nil {
// Don't return errors if the attestation collection fails, since
// the user didn't specify a TEEDevice.
device.AddAttestation(attestation, opts)
device.Close()
quoteProvider.AddAttestation(attestation, opts)
quoteProvider.Close()
return nil
}
// Add more devices here.
Expand Down
74 changes: 74 additions & 0 deletions client/attest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,3 +369,77 @@ func TestTdxDevice(t *testing.T) {
})
}
}

func TestTdxQuoteProvider(t *testing.T) {
rwc := test.GetTPM(t)
defer CheckedClose(t, rwc)

ak, err := AttestationKeyRSA(rwc)
if err != nil {
t.Fatalf("Failed to generate test AK: %v", err)
}

someNonce := []byte("some nonce")
var someNonce64 [64]byte
copy(someNonce64[:], someNonce)
var nonce64 [64]byte
copy(nonce64[:], []byte("noncey business"))
mockTdxQuoteProvider := tgtestclient.GetMockTdxQuoteProvider([]tgtest.TestCase{
{
Input: someNonce64,
Quote: tgtestdata.RawQuote,
},
{
Input: nonce64,
Quote: tgtestdata.RawQuote,
},
}, t)

testcases := []struct {
name string
opts AttestOpts
wantReportData [64]byte
wantErr string
}{
{
name: "Happy case no nonce",
opts: AttestOpts{
Nonce: someNonce,
TEEDevice: &TdxQuoteProvider{mockTdxQuoteProvider},
},
wantReportData: someNonce64,
},
{
name: "Happy case with nonce",
opts: AttestOpts{
Nonce: someNonce,
TEEDevice: &TdxQuoteProvider{mockTdxQuoteProvider},
TEENonce: nonce64[:],
},
wantReportData: nonce64,
},
{
name: "TEE nonce without TEE",
opts: AttestOpts{
Nonce: someNonce,
TEENonce: nonce64[:],
},
wantErr: "got non-nil TEENonce when TEEDevice is nil",
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
att, err := ak.Attest(tc.opts)
if (err == nil && tc.wantErr != "") || (err != nil && !strings.Contains(err.Error(), tc.wantErr)) {
t.Fatalf("Attest(%v) = %v, want %q", tc.opts, err, tc.wantErr)
}
// Successful attestation should include a TDX attestation.
if err == nil {
_, ok := att.GetTeeAttestation().(*pb.Attestation_TdxAttestation)
if !ok {
t.Fatalf("Attestation missing TDX attestation: %v", att.GetTeeAttestation())
}
}
})
}
}
4 changes: 2 additions & 2 deletions cmd/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ hardware and guarantees a fresh quote.
}
attestOpts.TEENonce = teeNonce
case Tdx:
attestOpts.TEEDevice, err = client.CreateTdxDevice()
attestOpts.TEEDevice, err = client.CreateTdxQuoteProvider()
if err != nil {
return fmt.Errorf("failed to open %s device: %v", Tdx, err)
return fmt.Errorf("failed to create %s quote provider: %v", Tdx, err)
}
attestOpts.TEENonce = teeNonce
case "":
Expand Down
7 changes: 3 additions & 4 deletions cmd/attest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,12 +341,11 @@ func TestTdxAttestTeeNonceFail(t *testing.T) {
}

// TEENonce with length less than 64 bytes.
tdxTestDevice := tgtestclient.GetTdxGuest([]tgtest.TestCase{
mockTdxQuoteProvider := tgtestclient.GetMockTdxQuoteProvider([]tgtest.TestCase{
{
Input: [64]byte{1, 2, 3, 4},
},
}, t)
defer tdxTestDevice.Close()

ak, err := client.AttestationKeyRSA(rwc)
if err != nil {
Expand All @@ -356,7 +355,7 @@ func TestTdxAttestTeeNonceFail(t *testing.T) {
attestopts := client.AttestOpts{
Nonce: []byte{1, 2, 3, 4},
TEENonce: []byte{1, 2, 3, 4},
TEEDevice: &client.TdxDevice{Device: tdxTestDevice},
TEEDevice: &client.TdxQuoteProvider{QuoteProvider: mockTdxQuoteProvider},
}
_, err = ak.Attest(attestopts)
if err == nil {
Expand All @@ -380,7 +379,7 @@ func TestHardwareAttestationPass(t *testing.T) {
teetech string
wanterr string
}{
{"TdxPass", "1234", "tdx", "failed to open tdx device"},
{"TdxPass", "1234", "tdx", "failed to create tdx quote provider"},
{"SevSnpPass", "1234", "sev-snp", "failed to open sev-snp device"},
}
for _, op := range tests {
Expand Down
2 changes: 1 addition & 1 deletion cmd/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func TestHwAttestationPass(t *testing.T) {
teetech string
wanterr string
}{
{"TdxPass", "1234", "tdx", "failed to open tdx device"},
{"TdxPass", "1234", "tdx", "failed to create tdx quote provider"},
{"SevSnpPass", "1234", "sev-snp", "failed to open sev-snp device"},
}
for _, op := range tests {
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/google/go-attestation v0.5.0
github.com/google/go-cmp v0.5.9
github.com/google/go-sev-guest v0.9.3
github.com/google/go-tdx-guest v0.2.3-0.20231011100059-4cf02bed9d33
github.com/google/go-tdx-guest v0.2.3-0.20231222042644-aeefcb0d0eb3
github.com/google/go-tpm v0.9.0
github.com/google/logger v1.1.1
google.golang.org/protobuf v1.31.0
Expand All @@ -21,6 +21,6 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/stretchr/testify v1.8.3 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/sys v0.15.0 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@ github.com/google/go-sev-guest v0.9.3 h1:GOJ+EipURdeWFl/YYdgcCxyPeMgQUWlI056iFkB
github.com/google/go-sev-guest v0.9.3/go.mod h1:hc1R4R6f8+NcJwITs0L90fYWTsBpd1Ix+Gur15sqHDs=
github.com/google/go-tdx-guest v0.2.3-0.20231011100059-4cf02bed9d33 h1:lRlUusuieEuqljjihCXb+Mr73VNitOYPJYWXzJKtBWs=
github.com/google/go-tdx-guest v0.2.3-0.20231011100059-4cf02bed9d33/go.mod h1:84ut3oago/BqPXD4ppiGXdkZNW3WFPkcyAO4my2hXdY=
github.com/google/go-tdx-guest v0.2.3-0.20231222042644-aeefcb0d0eb3 h1:0EpD00z41G8EjJsHmO54BoUb3izF8/hgUzAzmlo6mCk=
github.com/google/go-tdx-guest v0.2.3-0.20231222042644-aeefcb0d0eb3/go.mod h1:Iut2YE9wI7DbuNY9hFcExiUH5oeayMkHNgh/JpByzHQ=
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-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
Expand Down Expand Up @@ -765,6 +767,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down Expand Up @@ -959,6 +962,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
14 changes: 9 additions & 5 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -193,21 +193,21 @@ cloud.google.com/go/webrisk v1.9.2/go.mod h1:pY9kfDgAqxUpDBOrG4w8deLfhvJmejKB0qd
cloud.google.com/go/websecurityscanner v1.6.2/go.mod h1:7YgjuU5tun7Eg2kpKgGnDuEOXWIrh8x8lWrJT4zfmas=
cloud.google.com/go/workflows v1.12.1/go.mod h1:5A95OhD/edtOhQd/O741NSfIMezNTbCwLM1P1tBRGHM=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g=
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8=
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY=
github.com/google/go-sev-guest v0.8.0/go.mod h1:hc1R4R6f8+NcJwITs0L90fYWTsBpd1Ix+Gur15sqHDs=
github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
Expand All @@ -218,6 +218,7 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
Expand All @@ -238,12 +239,15 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
Expand Down

0 comments on commit 805756d

Please sign in to comment.