Skip to content

Commit

Permalink
Implement support for TPM-backed signing keys (#953)
Browse files Browse the repository at this point in the history
  • Loading branch information
charles-dyfis-net committed Apr 12, 2021
1 parent cbf0416 commit dea4dfc
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 30 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ List of contributors, in chronological order:
* Raphael Medaer (https://github.com/rmedaer)
* Raul Benencia (https://github.com/rul)
* Don Kuntz (https://github.com/dkuntz2)
* Charles Duffy (https://github.com/charles-dyfis-net)
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ require (
github.com/aws/aws-sdk-go v1.25.0
github.com/cheggaaa/pb v1.0.10
github.com/fatih/color v1.7.0 // indirect
github.com/folbricht/tpmk v0.1.2-0.20210411225238-7061339773c3
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 // indirect
github.com/gin-gonic/gin v1.1.5-0.20170702092826-d459835d2b07
github.com/google/go-tpm v0.1.2-0.20190306182045-7a7fe86fbbf2
github.com/h2non/filetype v1.0.5
github.com/jlaffaye/ftp v0.0.0-20180404123514-2403248fa8cc // indirect
github.com/kjk/lzma v0.0.0-20161016003348-3fd93898850d
Expand All @@ -32,7 +34,7 @@ require (
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d
github.com/ugorji/go v1.1.4
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0
golang.org/x/crypto v0.0.0-20180403160946-b2aa35443fbc
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
Expand Down
17 changes: 17 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ github.com/awalterschulze/gographviz v0.0.0-20160912181450-761fd5fbb34e h1:24jix
github.com/awalterschulze/gographviz v0.0.0-20160912181450-761fd5fbb34e/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs=
github.com/aws/aws-sdk-go v1.25.0 h1:MyXUdCesJLBvSSKYcaKeeEwxNUwUpG6/uqVYeH/Zzfo=
github.com/aws/aws-sdk-go v1.25.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/charles-dyfis-net/tpmk v0.1.2-0.20210411224552-7357a2171f47 h1:NMr7ZK1dpWN9SbaxUP2z6KkJX9Tci/Y3ZdtoyY3nECc=
github.com/charles-dyfis-net/tpmk v0.1.2-0.20210411224552-7357a2171f47/go.mod h1:OMV4y1gh5ibhzmF59bQOm6klTYfZOHpotZHiD7eA/SY=
github.com/cheggaaa/pb v1.0.10 h1:CNg2511WECXZ7Ja6jjyz9CMBpQOrMuP5+H5zfjgVi/Q=
github.com/cheggaaa/pb v1.0.10/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/folbricht/sshtest v0.1.0/go.mod h1:HuLqb6uP2Ca4k9AwHeeivT0GLMomsBzq2PNVWO2ZL58=
github.com/folbricht/tpmk v0.1.1/go.mod h1:RhWqouWQ0rBWZ/ReJdQ+xX9RSt37oT09e7HNVFSNMBM=
github.com/folbricht/tpmk v0.1.2-0.20210411225238-7061339773c3 h1:oRWZHiWpr89KGd6xkJaZp+HJ+WKLNS2Ub9ExC7OUew4=
github.com/folbricht/tpmk v0.1.2-0.20210411225238-7061339773c3/go.mod h1:OMV4y1gh5ibhzmF59bQOm6klTYfZOHpotZHiD7eA/SY=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 h1:AzN37oI0cOS+cougNAV9szl6CVoj2RYwzS3DpUQNtlY=
Expand All @@ -22,10 +28,14 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-tpm v0.1.2-0.20190306182045-7a7fe86fbbf2 h1:e1kceXf1XB12ZNTi2UZiXtb0+c8+zlMlQarLiTohoUY=
github.com/google/go-tpm v0.1.2-0.20190306182045-7a7fe86fbbf2/go.mod h1:OGEdc1XfzTyNEQyahgeXVq+E0lMq3Vu/Y3bT9EfpRnE=
github.com/google/go-tpm-tools v0.0.0-20190131232102-89d1c95730e5/go.mod h1:ApmLTU8fd5JJJ4J67y9sV16nOTR00GW2OabMwk7kSnE=
github.com/h2non/filetype v1.0.5 h1:Esu2EFM5vrzNynnGQpj0nxhCkzVQh2HRY7AXUh/dyJM=
github.com/h2non/filetype v1.0.5/go.mod h1:isekKqOuhMj+s/7r3rIeTErIRy4Rub5uBWHfvMusLMU=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jlaffaye/ftp v0.0.0-20180404123514-2403248fa8cc h1:lWFup/SOhwcpvRJIFqx/WQis5U4SrOSyWfSqvfdF09w=
github.com/jlaffaye/ftp v0.0.0-20180404123514-2403248fa8cc/go.mod h1:lli8NYPQOFy3O++YmYbqVgOcQ1JPCwdOy+5zSjKJ9qY=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
Expand Down Expand Up @@ -71,7 +81,10 @@ github.com/smira/go-ftp-protocol v0.0.0-20140829150050-066b75c2b70d h1:rvtR4+9N2
github.com/smira/go-ftp-protocol v0.0.0-20140829150050-066b75c2b70d/go.mod h1:Jm7yHrROA5tC42gyJ5EwiR8EWp0PUy0qOc4sE7Y8Uzo=
github.com/smira/go-xz v0.0.0-20150414201226-0c531f070014 h1:tne8XW3soRDJn4DIiqBc4jw+DPashtFMTSC9G0pC3ug=
github.com/smira/go-xz v0.0.0-20150414201226-0c531f070014/go.mod h1:smSuTvETRIkX95VAIWBdKoGJuUxif7NT7FgbkpVqOiA=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs=
Expand All @@ -82,12 +95,16 @@ github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlV
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM=
golang.org/x/crypto v0.0.0-20180403160946-b2aa35443fbc h1:Kx1Ke+iCR1aDjbWXgmEQGFxoHtNL49aRZGV7/+jJ41Y=
golang.org/x/crypto v0.0.0-20180403160946-b2aa35443fbc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576 h1:aUX/1G2gFSs4AsJJg2cL3HuoRhCSCz733FE5GUSuaT4=
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
Expand Down
2 changes: 1 addition & 1 deletion man/aptly.1
Original file line number Diff line number Diff line change
Expand Up @@ -1444,7 +1444,7 @@ GPG passphrase\-file for the key (warning: could be insecure)
.
.TP
\-\fBsecret\-keyring\fR=
GPG secret keyring to use (instead of default)
GPG secret keyring to use (instead of default); may be of the form \fBtpm://HANDLE?dev=DEVICE\fR to use a TPM-backed key if the selected \fBgpgProvider\fR is \fBinternal\fR, where \fbHANDLE\fR is of the form \fb0x81000003\fR, and \fBdev\fR is a (URL-escaped) value similar to \fB/dev/tpmrm0\fR (which happens to be the default if not given).
.
.TP
\-\fBskip\-contents\fR
Expand Down
119 changes: 91 additions & 28 deletions pgp/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@ import (
"fmt"
"io"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"syscall"
"time"

"github.com/folbricht/tpmk"
"github.com/google/go-tpm/tpmutil"
"github.com/pkg/errors"

"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/clearsign"
openpgp_errors "golang.org/x/crypto/openpgp/errors"
"golang.org/x/crypto/openpgp/packet"
Expand All @@ -39,12 +44,33 @@ type GoSigner struct {
passphrase, passphraseFile string
batch bool

tpmPrivateKey *tpmk.RSAPrivateKey
publicKeyring openpgp.EntityList
secretKeyring openpgp.EntityList
signer *openpgp.Entity
signerConfig *packet.Config
}

func findKey(keyRef string, keyring openpgp.EntityList) *openpgp.Entity {
for _, signer := range keyring {
key := KeyFromUint64(signer.PrimaryKey.KeyId)
if key.Matches(Key(keyRef)) {
return signer
}

if !validEntity(signer) {
continue
}

for name := range signer.Identities {
if strings.Contains(name, keyRef) {
return signer
}
}
}
return nil
}

// SetBatch controls whether we allowed to interact with user
func (g *GoSigner) SetBatch(batch bool) {
g.batch = batch
Expand Down Expand Up @@ -104,12 +130,56 @@ func (g *GoSigner) Init() error {
return errors.Wrap(err, "error loading public keyring")
}

g.secretKeyring, err = loadKeyRing(g.secretKeyringFile, false)
if err != nil {
return errors.Wrap(err, "error load secret keyring")
if strings.HasPrefix(g.secretKeyringFile, "tpm://") {
// Expected form of tpm://0x81000002 -- optionally with query parameters holding extra values
// f/e, ?dev=%2Fdev%2Ftpmrm1 to specify the device as /dev/tpmrm1; or ?dev=sim for simulator
tpmSecretUrl, err := url.Parse(g.secretKeyringFile)
if err != nil {
return errors.Wrap(err, "parsing TPM URI")
}
tpmQueryArgs := tpmSecretUrl.Query()
devStrings, hasDev := tpmQueryArgs["dev"]
tpmDevFilename := "/dev/tpmrm0"
if hasDev && len(devStrings) != 0 {
if len(devStrings) > 1 {
return errors.Errorf("Parsing TPM address, more than one device name found")
}
tpmDevFilename = devStrings[0]
}
tpmDev, err := tpmk.OpenDevice(tpmDevFilename)
if err != nil {
return errors.Wrap(err, "opening TPM device")
}
tpmHandleInt, err := strconv.ParseUint(tpmSecretUrl.Host, 0, 32)
if err != nil {
return errors.Wrap(err, "parsing TPM URI host as integer handle")
}
tpmHandle := tpmutil.Handle(tpmHandleInt)
privKey, err := tpmk.NewRSAPrivateKey(tpmDev, tpmHandle, g.passphrase)
if err != nil {
return errors.Wrap(err, "opening TPM key handle")
}
g.tpmPrivateKey = &privKey
} else {
g.secretKeyring, err = loadKeyRing(g.secretKeyringFile, false)
if err != nil {
return errors.Wrap(err, "error load secret keyring")
}
}

if g.keyRef == "" {
if g.secretKeyring == nil {
// Happens if our private key is TPM-backed; means we only have a public key
if g.keyRef == "" && len(g.publicKeyring) == 1 {
g.signer = g.publicKeyring[0]
} else if g.keyRef != "" {
g.signer = findKey(g.keyRef, g.publicKeyring)
if g.signer == nil {
return errors.Errorf("couldn't find key for key reference %+v in public keyring", g.keyRef)
}
} else {
return errors.Errorf("must either only have our signing key in the public keyring, or provide the identity of the signing key when in tpm mode")
}
} else if g.keyRef == "" {
// no key reference, pick the first key
for _, signer := range g.secretKeyring {
if !validEntity(signer) {
Expand All @@ -124,28 +194,9 @@ func (g *GoSigner) Init() error {
return fmt.Errorf("looks like there are no keys in gpg, please create one (official manual: http://www.gnupg.org/gph/en/manual.html)")
}
} else {
pickKeyLoop:
for _, signer := range g.secretKeyring {
key := KeyFromUint64(signer.PrimaryKey.KeyId)
if key.Matches(Key(g.keyRef)) {
g.signer = signer
break
}

if !validEntity(signer) {
continue
}

for name := range signer.Identities {
if strings.Contains(name, g.keyRef) {
g.signer = signer
break pickKeyLoop
}
}
}

g.signer = findKey(g.keyRef, g.secretKeyring)
if g.signer == nil {
return errors.Errorf("couldn't find key for key reference %v", g.keyRef)
return errors.Errorf("couldn't find key for key reference %v in private keyring", g.keyRef)
}
}

Expand Down Expand Up @@ -232,9 +283,21 @@ func (g *GoSigner) DetachedSign(source string, destination string) error {
}
defer signature.Close()

err = openpgp.ArmoredDetachSign(signature, g.signer, message, g.signerConfig)
if err != nil {
return errors.Wrap(err, "error creating detached signature")
if g.tpmPrivateKey != nil {
encoder, err := armor.Encode(signature, openpgp.SignatureType, nil)
if err != nil {
return errors.Wrap(err, "error creating armoring encoder")
}
defer encoder.Close()
err = tpmk.OpenPGPDetachSign(encoder, g.signer, message, nil, g.tpmPrivateKey)
if err != nil {
return errors.Wrap(err, "error creating detached signature with TPM-backed key")
}
} else {
err = openpgp.ArmoredDetachSign(signature, g.signer, message, g.signerConfig)
if err != nil {
return errors.Wrap(err, "error creating detached signature")
}
}

return nil
Expand Down

0 comments on commit dea4dfc

Please sign in to comment.