mirror of
https://github.com/kairos-io/tpm-helpers.git
synced 2025-09-05 23:21:41 +00:00
Fixup tests, add testing pipelines and small refactors
Signed-off-by: Ettore Di Giacinto <edigiacinto@suse.com>
This commit is contained in:
37
.github/workflows/static_analisys.yaml
vendored
Normal file
37
.github/workflows/static_analisys.yaml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Static Analysis
|
||||
on: [push, pull_request]
|
||||
concurrency:
|
||||
group: static-analysis-${{ github.head_ref || github.ref }}-${{ github.repository }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
checks:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
- name: Get dependencies
|
||||
run: |
|
||||
# Needed for github.com/google/go-tspi/tspi
|
||||
sudo apt-get install libtspi-dev
|
||||
go install golang.org/x/tools/cmd/goimports@latest
|
||||
go install github.com/fzipp/gocyclo/cmd/gocyclo@latest
|
||||
go install golang.org/x/lint/golint@latest
|
||||
go install honnef.co/go/tools/cmd/staticcheck@v0.2.0
|
||||
|
||||
- name: Vet
|
||||
run: go vet -tags ci ./...
|
||||
|
||||
- name: Goimports
|
||||
run: test -z $(goimports -e -d . | tee /dev/stderr)
|
||||
|
||||
- name: Gocyclo
|
||||
run: gocyclo -over 30 .
|
||||
|
||||
- name: Golint
|
||||
run: golint -set_exit_status $(go list -tags ci ./...)
|
||||
|
||||
- name: Staticcheck
|
||||
run: staticcheck -go 1.12 ./...
|
27
.github/workflows/tests.yaml
vendored
Normal file
27
.github/workflows/tests.yaml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Tests
|
||||
on: [push, pull_request]
|
||||
concurrency:
|
||||
group: tests-${{ github.head_ref || github.ref }}-${{ github.repository }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
- name: Get dependencies
|
||||
run: |
|
||||
# Needed for github.com/google/go-tspi/tspi
|
||||
# in opensuse this is trousers-devel
|
||||
sudo add-apt-repository ppa:smoser/swtpm
|
||||
sudo apt-get update
|
||||
sudo apt-get install libtspi-dev swtpm
|
||||
swtpm socket --server type=unixio,path=/tmp/tpm-server --ctrl type=unixio,path=/tmp/tpm-ctrl --tpm2 -d
|
||||
go get github.com/onsi/ginkgo/v2/ginkgo
|
||||
go get github.com/onsi/gomega/...
|
||||
- name: Run tests
|
||||
run: |
|
||||
TPM_SOCKET=/tmp/tpm-ctrl ginkgo -r ./...
|
@@ -4,6 +4,7 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// FakeTPM is a wrapper for fake TPM devices
|
||||
type FakeTPM struct {
|
||||
io.ReadWriteCloser
|
||||
}
|
||||
@@ -14,4 +15,10 @@ var fixedLog = []byte{0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x20, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x33, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x2, 0x0, 0x2, 0x1, 0x0, 0x0, 0x0, 0xb, 0x0, 0x20, 0x0, 0x0}
|
||||
|
||||
// MeasurementLog returns static log data to comply to TPM interface
|
||||
func (*FakeTPM) MeasurementLog() ([]byte, error) { return fixedLog, nil }
|
||||
|
||||
// Fake returns a fake TPM-satisfying interface from a ReadWriteCloser
|
||||
func Fake(rw io.ReadWriteCloser) *FakeTPM {
|
||||
return &FakeTPM{ReadWriteCloser: rw}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package backend
|
||||
|
||||
import "net"
|
||||
|
||||
// Socket returns a fake TPM interface from a unix socket
|
||||
func Socket(f string) (*FakeTPM, error) {
|
||||
conn, err := net.Dial("unix", f)
|
||||
if err != nil {
|
||||
|
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
var _ = Describe("SWTPM", func() {
|
||||
socket := os.Getenv("SWTPM_SOCKET")
|
||||
socket := os.Getenv("TPM_SOCKET")
|
||||
Context("opening socket connection", func() {
|
||||
It("fails on invalid files", func() {
|
||||
|
||||
|
43
config.go
43
config.go
@@ -1,35 +1,64 @@
|
||||
package tpm
|
||||
|
||||
import "github.com/google/go-attestation/attest"
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
type Config struct {
|
||||
"github.com/google/go-attestation/attest"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
emulated bool
|
||||
commandChannel attest.CommandChannelTPM20
|
||||
seed int64
|
||||
|
||||
cacerts []byte
|
||||
header http.Header
|
||||
}
|
||||
|
||||
type Option func(c *Config) error
|
||||
// Option is a generic option for TPM configuration
|
||||
type Option func(c *config) error
|
||||
|
||||
var Emulated Option = func(c *Config) error {
|
||||
// Emulated sets an emulated device in place of a real native TPM device.
|
||||
// Note, the emulated device is embedded and it is unsafe.
|
||||
// Should just be used for testing.
|
||||
var Emulated Option = func(c *config) error {
|
||||
c.emulated = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithCAs sets the root CAs for the request
|
||||
func WithCAs(ca []byte) Option {
|
||||
return func(c *config) error {
|
||||
c.cacerts = ca
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeader sets a specific header for the request
|
||||
func WithHeader(header http.Header) Option {
|
||||
return func(c *config) error {
|
||||
c.header = header
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSeed sets a permanent seed. Used with TPM emulated device.
|
||||
func WithSeed(s int64) Option {
|
||||
return func(c *Config) error {
|
||||
return func(c *config) error {
|
||||
c.seed = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithCommandChannel overrides the TPM command channel
|
||||
func WithCommandChannel(cc attest.CommandChannelTPM20) Option {
|
||||
return func(c *Config) error {
|
||||
return func(c *config) error {
|
||||
c.commandChannel = cc
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) Apply(opts ...Option) error {
|
||||
func (c *config) apply(opts ...Option) error {
|
||||
for _, o := range opts {
|
||||
if err := o(c); err != nil {
|
||||
return err
|
||||
|
22
get.go
22
get.go
@@ -16,11 +16,21 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Get(cacerts []byte, url string, header http.Header, opts ...Option) ([]byte, error) {
|
||||
// Get retrieves a message from a remote ws server after
|
||||
// a successfully process of the TPM challenge
|
||||
func Get(url string, opts ...Option) ([]byte, error) {
|
||||
c := &config{}
|
||||
c.apply(opts...)
|
||||
|
||||
header := c.header
|
||||
if c.header == nil {
|
||||
header = http.Header{}
|
||||
}
|
||||
|
||||
dialer := websocket.DefaultDialer
|
||||
if len(cacerts) > 0 {
|
||||
if len(c.cacerts) > 0 {
|
||||
pool := x509.NewCertPool()
|
||||
pool.AppendCertsFromPEM(cacerts)
|
||||
pool.AppendCertsFromPEM(c.cacerts)
|
||||
dialer = &websocket.Dialer{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
HandshakeTimeout: 45 * time.Second,
|
||||
@@ -30,9 +40,6 @@ func Get(cacerts []byte, url string, header http.Header, opts ...Option) ([]byte
|
||||
}
|
||||
}
|
||||
|
||||
c := &Config{}
|
||||
c.Apply(opts...)
|
||||
|
||||
attestationData, aikBytes, err := getAttestationData(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -48,9 +55,6 @@ func Get(cacerts []byte, url string, header http.Header, opts ...Option) ([]byte
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if header == nil {
|
||||
header = http.Header{}
|
||||
}
|
||||
header.Add("Authorization", token)
|
||||
wsURL := strings.Replace(url, "http", "ws", 1)
|
||||
logrus.Infof("Using TPMHash %s to dial %s", hash, wsURL)
|
||||
|
2
go.mod
2
go.mod
@@ -5,6 +5,7 @@ go 1.17
|
||||
require (
|
||||
github.com/google/certificate-transparency-go v1.1.2
|
||||
github.com/google/go-attestation v0.4.3
|
||||
github.com/google/go-tpm-tools v0.3.2
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/onsi/ginkgo/v2 v2.1.3
|
||||
github.com/onsi/gomega v1.17.0
|
||||
@@ -14,7 +15,6 @@ require (
|
||||
|
||||
require (
|
||||
github.com/google/go-tpm v0.3.3 // indirect
|
||||
github.com/google/go-tpm-tools v0.3.2 // indirect
|
||||
github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad // indirect
|
||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b // indirect
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 // indirect
|
||||
|
1
go.sum
1
go.sum
@@ -331,7 +331,6 @@ github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51B
|
||||
github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0=
|
||||
github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4=
|
||||
github.com/google/go-tpm-tools v0.2.1/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4=
|
||||
github.com/google/go-tpm-tools v0.3.1 h1:AFlmenDrIe0WU5AvpbfGFOLprTJTg/fCwmTyFdDEjbM=
|
||||
github.com/google/go-tpm-tools v0.3.1/go.mod h1:PSg+r5hSZI5tP3X7LBQx2sW1VSZUqZHBSrKyDqrB21U=
|
||||
github.com/google/go-tpm-tools v0.3.2 h1:2KbPTrwqLTJUZIZNoMSd1UlzoUvmSNbtm14WuDovZjw=
|
||||
github.com/google/go-tpm-tools v0.3.2/go.mod h1:FYUkglac8nSi15sPnNrP9fha3A6PRb6qzEVA+Zkh8SA=
|
||||
|
25
tpm.go
25
tpm.go
@@ -11,6 +11,9 @@ import (
|
||||
"github.com/rancher-sandbox/go-tpm/backend"
|
||||
)
|
||||
|
||||
// ResolveToken is just syntax sugar around GetPubHash.
|
||||
// If the token provided is in EK's form it just returns it, otherwise
|
||||
// retrieves the pubhash
|
||||
func ResolveToken(token string, opts ...Option) (bool, string, error) {
|
||||
if !strings.HasPrefix(token, "tpm://") {
|
||||
return false, token, nil
|
||||
@@ -20,10 +23,10 @@ func ResolveToken(token string, opts ...Option) (bool, string, error) {
|
||||
return true, hash, err
|
||||
}
|
||||
|
||||
// GetPubHash returns the EK's pub hash
|
||||
func GetPubHash(opts ...Option) (string, error) {
|
||||
|
||||
c := &Config{}
|
||||
c.Apply(opts...)
|
||||
c := &config{}
|
||||
c.apply(opts...)
|
||||
|
||||
ek, err := getEK(c)
|
||||
if err != nil {
|
||||
@@ -38,7 +41,7 @@ func GetPubHash(opts ...Option) (string, error) {
|
||||
return hash, nil
|
||||
}
|
||||
|
||||
func getTPM(c *Config) (*attest.TPM, error) {
|
||||
func getTPM(c *config) (*attest.TPM, error) {
|
||||
|
||||
cfg := &attest.OpenConfig{
|
||||
TPMVersion: attest.TPMVersion20,
|
||||
@@ -52,20 +55,20 @@ func getTPM(c *Config) (*attest.TPM, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.CommandChannel = &backend.FakeTPM{ReadWriteCloser: sim}
|
||||
cfg.CommandChannel = backend.Fake(sim)
|
||||
} else {
|
||||
sim, err := simulator.GetWithFixedSeedInsecure(c.seed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.CommandChannel = &backend.FakeTPM{ReadWriteCloser: sim}
|
||||
cfg.CommandChannel = backend.Fake(sim)
|
||||
}
|
||||
|
||||
return attest.OpenTPM(cfg)
|
||||
|
||||
}
|
||||
|
||||
func getEK(c *Config) (*attest.EK, error) {
|
||||
func getEK(c *config) (*attest.EK, error) {
|
||||
var err error
|
||||
|
||||
tpm, err := getTPM(c)
|
||||
@@ -86,7 +89,7 @@ func getEK(c *Config) (*attest.EK, error) {
|
||||
return &eks[0], nil
|
||||
}
|
||||
|
||||
func getToken(data *AttestationData) (string, error) {
|
||||
func getToken(data *attestationData) (string, error) {
|
||||
bytes, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("marshalling attestation data: %w", err)
|
||||
@@ -95,7 +98,7 @@ func getToken(data *AttestationData) (string, error) {
|
||||
return "Bearer TPM" + base64.StdEncoding.EncodeToString(bytes), nil
|
||||
}
|
||||
|
||||
func getAttestationData(c *Config) (*AttestationData, []byte, error) {
|
||||
func getAttestationData(c *config) (*attestationData, []byte, error) {
|
||||
var err error
|
||||
|
||||
tpm, err := getTPM(c)
|
||||
@@ -121,7 +124,7 @@ func getAttestationData(c *Config) (*AttestationData, []byte, error) {
|
||||
}
|
||||
|
||||
ek := &eks[0]
|
||||
ekBytes, err := EncodeEK(ek)
|
||||
ekBytes, err := encodeEK(ek)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -131,7 +134,7 @@ func getAttestationData(c *Config) (*AttestationData, []byte, error) {
|
||||
return nil, nil, fmt.Errorf("marshaling AK: %w", err)
|
||||
}
|
||||
|
||||
return &AttestationData{
|
||||
return &attestationData{
|
||||
EK: ekBytes,
|
||||
AK: ¶ms,
|
||||
}, aikBytes, nil
|
||||
|
@@ -23,21 +23,22 @@ import (
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
"github.com/google/go-attestation/attest"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type AttestationData struct {
|
||||
type attestationData struct {
|
||||
EK []byte
|
||||
AK *attest.AttestationParameters
|
||||
}
|
||||
|
||||
// Challenge represent the struct returned from the ws server,
|
||||
// used to resolve the TPM challenge.
|
||||
type Challenge struct {
|
||||
EC *attest.EncryptedCredential
|
||||
}
|
||||
|
||||
type KeyData struct {
|
||||
Keys []string `json:"keys"`
|
||||
}
|
||||
|
||||
// ChallengeResponse represent the struct returned to the ws server
|
||||
// as a challenge response.
|
||||
type ChallengeResponse struct {
|
||||
Secret []byte
|
||||
}
|
||||
@@ -52,7 +53,7 @@ func getPubHash(ek *attest.EK) (string, error) {
|
||||
return hashEncoded, nil
|
||||
}
|
||||
|
||||
func EncodeEK(ek *attest.EK) ([]byte, error) {
|
||||
func encodeEK(ek *attest.EK) ([]byte, error) {
|
||||
if ek.Certificate != nil {
|
||||
return pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
@@ -78,3 +79,36 @@ func pubBytes(ek *attest.EK) ([]byte, error) {
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// DecodeEK decodes EK pem bytes to attest.EK
|
||||
func DecodeEK(pemBytes []byte) (*attest.EK, error) {
|
||||
block, _ := pem.Decode(pemBytes)
|
||||
|
||||
if block == nil {
|
||||
return nil, errors.New("invalid pemBytes")
|
||||
}
|
||||
|
||||
switch block.Type {
|
||||
case "CERTIFICATE":
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing certificate: %v", err)
|
||||
}
|
||||
return &attest.EK{
|
||||
Certificate: cert,
|
||||
Public: cert.PublicKey,
|
||||
}, nil
|
||||
|
||||
case "PUBLIC KEY":
|
||||
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing ecdsa public key: %v", err)
|
||||
}
|
||||
|
||||
return &attest.EK{
|
||||
Public: pub,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid pem type: %s", block.Type)
|
||||
}
|
||||
|
47
tpm_test.go
47
tpm_test.go
@@ -9,30 +9,6 @@ import (
|
||||
. "github.com/rancher-sandbox/go-tpm/backend"
|
||||
)
|
||||
|
||||
// In order to run this suite a swtpm socket is required. e.g.:
|
||||
// swtpm socket --server type=unixio,path=/tmp/tpm-server --ctrl type=unixio,path=/tmp/tpm-ctrl --tpm2
|
||||
// SWTPM_SOCKET=/tmp/tpm-ctrl ginkgo -r ./
|
||||
|
||||
var _ = Describe("TPM with SWTPM", func() {
|
||||
socket := os.Getenv("SWTPM_SOCKET")
|
||||
Context("opening socket connection", func() {
|
||||
// Note, this doesn't work
|
||||
PIt("dials in just fine", func() {
|
||||
if socket == "" {
|
||||
Skip("No socket file specified")
|
||||
}
|
||||
|
||||
b, err := Socket(socket)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
str, err := GetPubHash(WithCommandChannel(b))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(str).ToNot(BeEmpty())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Simulated TPM", func() {
|
||||
Context("opening socket connection", func() {
|
||||
It("dials in just fine", func() {
|
||||
@@ -48,7 +24,30 @@ var _ = Describe("Simulated TPM", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
str2, err := GetPubHash(Emulated, WithSeed(1))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
str3, err := GetPubHash(Emulated, WithSeed(2))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(str).To(Equal(str2))
|
||||
Expect(str).ToNot(Equal(str3))
|
||||
})
|
||||
})
|
||||
|
||||
Context("from a socket", func() {
|
||||
// In order to run this test a swtpm socket is required. e.g.:
|
||||
// swtpm socket --server type=unixio,path=/tmp/tpm-server --ctrl type=unixio,path=/tmp/tpm-ctrl --tpm2
|
||||
// TPM_SOCKET=/tmp/tpm-ctrl ginkgo -r ./
|
||||
|
||||
socket := os.Getenv("TPM_SOCKET")
|
||||
It("gets pubhash", func() {
|
||||
if socket == "" {
|
||||
Skip("No socket file specified")
|
||||
}
|
||||
|
||||
b, err := Socket(socket)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
str, err := GetPubHash(WithCommandChannel(b))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(str).ToNot(BeEmpty())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user