@@ -4,13 +4,17 @@ import (
44 "bytes"
55 "fmt"
66 "io"
7+ "net/url"
78 "os"
89 "path/filepath"
910 "sort"
11+ "strconv"
1012 "strings"
1113 "syscall"
1214 "time"
1315
16+ "github.com/folbricht/tpmk"
17+ "github.com/google/go-tpm/tpmutil"
1418 "github.com/pkg/errors"
1519
1620 "github.com/ProtonMail/go-crypto/openpgp"
@@ -38,12 +42,33 @@ type GoSigner struct {
3842 passphrase , passphraseFile string
3943 batch bool
4044
45+ tpmPrivateKey * tpmk.RSAPrivateKey
4146 publicKeyring openpgp.EntityList
4247 secretKeyring openpgp.EntityList
4348 signer * openpgp.Entity
4449 signerConfig * packet.Config
4550}
4651
52+ func findKey (keyRef string , keyring openpgp.EntityList ) * openpgp.Entity {
53+ for _ , signer := range keyring {
54+ key := KeyFromUint64 (signer .PrimaryKey .KeyId )
55+ if key .Matches (Key (keyRef )) {
56+ return signer
57+ }
58+
59+ if ! validEntity (signer ) {
60+ continue
61+ }
62+
63+ for name := range signer .Identities {
64+ if strings .Contains (name , keyRef ) {
65+ return signer
66+ }
67+ }
68+ }
69+ return nil
70+ }
71+
4772// SetBatch controls whether we allowed to interact with user, for example
4873// for getting the passphrase from stdin.
4974func (g * GoSigner ) SetBatch (batch bool ) {
@@ -104,12 +129,56 @@ func (g *GoSigner) Init() error {
104129 return errors .Wrap (err , "error loading public keyring" )
105130 }
106131
107- g .secretKeyring , err = loadKeyRing (g .secretKeyringFile , false )
108- if err != nil {
109- return errors .Wrap (err , "error load secret keyring" )
132+ if strings .HasPrefix (g .secretKeyringFile , "tpm://" ) {
133+ // Expected form of tpm://0x81000002 -- optionally with query parameters holding extra values
134+ // f/e, ?dev=%2Fdev%2Ftpmrm1 to specify the device as /dev/tpmrm1; or ?dev=sim for simulator
135+ tpmSecretURL , err := url .Parse (g .secretKeyringFile )
136+ if err != nil {
137+ return errors .Wrap (err , "parsing TPM URI" )
138+ }
139+ tpmQueryArgs := tpmSecretURL .Query ()
140+ devStrings , hasDev := tpmQueryArgs ["dev" ]
141+ tpmDevFilename := "/dev/tpmrm0"
142+ if hasDev && len (devStrings ) != 0 {
143+ if len (devStrings ) > 1 {
144+ return errors .Errorf ("Parsing TPM address, more than one device name found" )
145+ }
146+ tpmDevFilename = devStrings [0 ]
147+ }
148+ tpmDev , err := tpmk .OpenDevice (tpmDevFilename )
149+ if err != nil {
150+ return errors .Wrap (err , "opening TPM device" )
151+ }
152+ tpmHandleInt , err := strconv .ParseUint (tpmSecretURL .Host , 0 , 32 )
153+ if err != nil {
154+ return errors .Wrap (err , "parsing TPM URI host as integer handle" )
155+ }
156+ tpmHandle := tpmutil .Handle (tpmHandleInt )
157+ privKey , err := tpmk .NewRSAPrivateKey (tpmDev , tpmHandle , g .passphrase )
158+ if err != nil {
159+ return errors .Wrap (err , "opening TPM key handle" )
160+ }
161+ g .tpmPrivateKey = & privKey
162+ } else {
163+ g .secretKeyring , err = loadKeyRing (g .secretKeyringFile , false )
164+ if err != nil {
165+ return errors .Wrap (err , "error load secret keyring" )
166+ }
110167 }
111168
112- if g .keyRef == "" {
169+ if g .secretKeyring == nil {
170+ // Happens if our private key is TPM-backed; means we only have a public key
171+ if g .keyRef == "" && len (g .publicKeyring ) == 1 {
172+ g .signer = g .publicKeyring [0 ]
173+ } else if g .keyRef != "" {
174+ g .signer = findKey (g .keyRef , g .publicKeyring )
175+ if g .signer == nil {
176+ return errors .Errorf ("couldn't find key for key reference %+v in public keyring" , g .keyRef )
177+ }
178+ } else {
179+ 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" )
180+ }
181+ } else if g .keyRef == "" {
113182 // no key reference, pick the first key
114183 for _ , signer := range g .secretKeyring {
115184 if ! validEntity (signer ) {
@@ -124,28 +193,9 @@ func (g *GoSigner) Init() error {
124193 return fmt .Errorf ("looks like there are no keys in gpg, please create one (official manual: http://www.gnupg.org/gph/en/manual.html)" )
125194 }
126195 } else {
127- pickKeyLoop:
128- for _ , signer := range g .secretKeyring {
129- key := KeyFromUint64 (signer .PrimaryKey .KeyId )
130- if key .Matches (Key (g .keyRef )) {
131- g .signer = signer
132- break
133- }
134-
135- if ! validEntity (signer ) {
136- continue
137- }
138-
139- for name := range signer .Identities {
140- if strings .Contains (name , g .keyRef ) {
141- g .signer = signer
142- break pickKeyLoop
143- }
144- }
145- }
146-
196+ g .signer = findKey (g .keyRef , g .secretKeyring )
147197 if g .signer == nil {
148- return errors .Errorf ("couldn't find key for key reference %v" , g .keyRef )
198+ return errors .Errorf ("couldn't find key for key reference %v in private keyring " , g .keyRef )
149199 }
150200 }
151201
@@ -232,9 +282,21 @@ func (g *GoSigner) DetachedSign(source string, destination string) error {
232282 }
233283 defer signature .Close ()
234284
235- err = openpgp .ArmoredDetachSign (signature , g .signer , message , g .signerConfig )
236- if err != nil {
237- return errors .Wrap (err , "error creating detached signature" )
285+ if g .tpmPrivateKey != nil {
286+ encoder , err := armor .Encode (signature , openpgp .SignatureType , nil )
287+ if err != nil {
288+ return errors .Wrap (err , "error creating armoring encoder" )
289+ }
290+ defer encoder .Close ()
291+ err = tpmk .OpenPGPDetachSign (encoder , g .signer , message , nil , g .tpmPrivateKey )
292+ if err != nil {
293+ return errors .Wrap (err , "error creating detached signature with TPM-backed key" )
294+ }
295+ } else {
296+ err = openpgp .ArmoredDetachSign (signature , g .signer , message , g .signerConfig )
297+ if err != nil {
298+ return errors .Wrap (err , "error creating detached signature" )
299+ }
238300 }
239301
240302 return nil
0 commit comments