diff --git a/go.mod b/go.mod index 71d85bf..8c6e3f8 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,17 @@ module github.com/smlx/piv-agent go 1.19 require ( + filippo.io/nistec v0.0.3 github.com/ProtonMail/go-crypto v0.0.0-20230316153859-cb82d937a5d9 github.com/alecthomas/kong v0.9.0 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/davecgh/go-spew v1.1.1 github.com/gen2brain/beeep v0.0.0-20200526185328-e9c15c258e28 - github.com/go-piv/piv-go v1.11.0 - github.com/golang/mock v1.6.0 + github.com/go-piv/piv-go/v2 v2.2.0 github.com/smlx/fsm v0.2.1 github.com/twpayne/go-pinentry-minimal v0.0.0-20220113210447-2a5dc4396c2a github.com/x13a/go-launch v0.0.0-20210715084817-fd409384939b + go.uber.org/mock v0.4.0 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.25.0 golang.org/x/sync v0.7.0 diff --git a/go.sum b/go.sum index c9d9c9a..53541c9 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +filippo.io/nistec v0.0.3 h1:h336Je2jRDZdBCLy2fLDUd9E2unG32JLwcJi0JQE9Cw= +filippo.io/nistec v0.0.3/go.mod h1:84fxC9mi+MhC2AERXI4LSa8cmSVOzrFikg6hZ4IfCyw= github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU= github.com/alecthomas/kong v0.9.0 h1:G5diXxc85KvoV2f0ZRVuMsi45IrBgx9zDNGNj165aPA= github.com/alecthomas/kong v0.9.0/go.mod h1:Y47y5gKfHp1hDc7CH7OeXgLIpp+Q2m1Ni0L5s3bI8Os= @@ -12,14 +14,12 @@ 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= github.com/gen2brain/beeep v0.0.0-20200526185328-e9c15c258e28 h1:M2Zt3G2w6Q57GZndOYk42p7RvMeO8izO8yKTfIxGqxA= github.com/gen2brain/beeep v0.0.0-20200526185328-e9c15c258e28/go.mod h1:ElSskYZe3oM8kThaHGJ+kiN2yyUMVXMZ7WxF9QqLDS8= -github.com/go-piv/piv-go v1.11.0 h1:5vAaCdRTFSIW4PeqMbnsDlUZ7odMYWnHBDGdmtU/Zhg= -github.com/go-piv/piv-go v1.11.0/go.mod h1:NZ2zmjVkfFaL/CF8cVQ/pXdXtuj110zEKGdJM6fJZZM= +github.com/go-piv/piv-go/v2 v2.2.0 h1:kOcyd7+JiKLF1BtGUO6vg7XlDJLhchinoMuksOpWBEI= +github.com/go-piv/piv-go/v2 v2.2.0/go.mod h1:ShZi74nnrWNQEdWzRUd/3cSig3uNOcEZp+EWl0oewnI= github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE= github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4/go.mod h1:kW3HQ4UdaAyrUCSSDR4xUzBKW6O2iA4uHhk7AtyYp10= github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -40,41 +40,34 @@ github.com/twpayne/go-pinentry-minimal v0.0.0-20220113210447-2a5dc4396c2a h1:a1b github.com/twpayne/go-pinentry-minimal v0.0.0-20220113210447-2a5dc4396c2a/go.mod h1:ARJJXqNuaxVS84jX6ST52hQh0TtuQZWABhTe95a6BI4= github.com/x13a/go-launch v0.0.0-20210715084817-fd409384939b h1:rpNT9cyxH8nsCM8htO1SLhrehyt74GFczE9s/O6WkfE= github.com/x13a/go-launch v0.0.0-20210715084817-fd409384939b/go.mod h1:kfVYr1hMcmOVxZt+2kFzCXf/YRX9Cz+F1QkijZQMaMM= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -96,10 +89,7 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/assuan/assuan_test.go b/internal/assuan/assuan_test.go index bf37777..5d3f85f 100644 --- a/internal/assuan/assuan_test.go +++ b/internal/assuan/assuan_test.go @@ -17,12 +17,12 @@ import ( "github.com/ProtonMail/go-crypto/openpgp/ecdsa" "github.com/ProtonMail/go-crypto/openpgp/packet" "github.com/davecgh/go-spew/spew" - "github.com/golang/mock/gomock" "github.com/smlx/piv-agent/internal/assuan" "github.com/smlx/piv-agent/internal/keyservice/gpg" "github.com/smlx/piv-agent/internal/mock" "github.com/smlx/piv-agent/internal/notify" "github.com/smlx/piv-agent/internal/securitykey" + "go.uber.org/mock/gomock" "go.uber.org/zap" "golang.org/x/crypto/cryptobyte" "golang.org/x/crypto/cryptobyte/asn1" diff --git a/internal/assuan/readkey.go b/internal/assuan/readkey.go index becf477..fc49d80 100644 --- a/internal/assuan/readkey.go +++ b/internal/assuan/readkey.go @@ -26,7 +26,11 @@ func readKeyData(pub crypto.PublicKey) (string, error) { case *ecdsa.PublicKey: switch k.Curve { case elliptic.P256(): - q := elliptic.Marshal(k.Curve, k.X, k.Y) + ecdhPubKey, err := k.ECDH() + if err != nil { + return "", fmt.Errorf("couldn't convert pub key to ecdh.PublicKey: %v", err) + } + q := ecdhPubKey.Bytes() qLen := len(q) q = PercentEncodeSExp(q) return fmt.Sprintf( diff --git a/internal/keyservice/gpg/ecdhkey.go b/internal/keyservice/gpg/ecdhkey.go index 814e25b..af8bcdb 100644 --- a/internal/keyservice/gpg/ecdhkey.go +++ b/internal/keyservice/gpg/ecdhkey.go @@ -3,11 +3,11 @@ package gpg import ( "crypto" "crypto/ecdsa" - "crypto/elliptic" "fmt" "io" "regexp" + "filippo.io/nistec" "github.com/smlx/piv-agent/internal/assuan" ) @@ -27,15 +27,18 @@ func (k *ECDHKey) Decrypt(_ io.Reader, sexp []byte, ciphertext := matches[0][2] // undo the buggy encoding sent by gpg ciphertext = assuan.PercentDecodeSExp(ciphertext) - // unmarshal the ephemeral key - ephPubX, ephPubY := elliptic.Unmarshal(elliptic.P256(), ciphertext) - if ephPubX == nil { - return nil, fmt.Errorf("couldn't unmarshal ephemeral key") + // perform scalar multiplication + sharedPoint := nistec.NewP256Point() + _, err := sharedPoint.SetBytes(ciphertext) + if err != nil { + return nil, fmt.Errorf("couldn't set point bytes: %v", err) + } + _, err = sharedPoint.ScalarMult(sharedPoint, k.ecdsa.D.Bytes()) + if err != nil { + return nil, fmt.Errorf("couldn't perform scalar mult: %v", err) } - // perform the scalar mult - sharedX, sharedY := k.ecdsa.ScalarMult(ephPubX, ephPubY, k.ecdsa.D.Bytes()) // marshal, encode, and return the result - shared := elliptic.Marshal(elliptic.P256(), sharedX, sharedY) + shared := sharedPoint.Bytes() sharedLen := len(shared) shared = assuan.PercentEncodeSExp(shared) return []byte(fmt.Sprintf("D (5:value%d:%s)\nOK\n", sharedLen, shared)), nil diff --git a/internal/keyservice/gpg/keyservice_test.go b/internal/keyservice/gpg/keyservice_test.go index b3f374d..06b277b 100644 --- a/internal/keyservice/gpg/keyservice_test.go +++ b/internal/keyservice/gpg/keyservice_test.go @@ -4,9 +4,9 @@ import ( "encoding/hex" "testing" - "github.com/golang/mock/gomock" "github.com/smlx/piv-agent/internal/keyservice/gpg" "github.com/smlx/piv-agent/internal/mock" + "go.uber.org/mock/gomock" "go.uber.org/zap" ) diff --git a/internal/keyservice/piv/ecdhkey.go b/internal/keyservice/piv/ecdhkey.go index 7acdafe..b17383e 100644 --- a/internal/keyservice/piv/ecdhkey.go +++ b/internal/keyservice/piv/ecdhkey.go @@ -2,14 +2,13 @@ package piv import ( "crypto" - "crypto/ecdsa" - "crypto/elliptic" + "crypto/ecdh" "fmt" "io" "regexp" "sync" - pivgo "github.com/go-piv/piv-go/piv" + pivgo "github.com/go-piv/piv-go/v2/piv" "github.com/smlx/piv-agent/internal/assuan" ) @@ -34,18 +33,12 @@ func (k *ECDHKey) Decrypt(_ io.Reader, sexp []byte, // undo the buggy encoding sent by gpg ciphertext = assuan.PercentDecodeSExp(ciphertext) // unmarshal the ephemeral key - ephPubX, ephPubY := elliptic.Unmarshal(elliptic.P256(), ciphertext) - if ephPubX == nil { - return nil, fmt.Errorf("couldn't unmarshal ephemeral key") - } - // create the public key - ephPub := ecdsa.PublicKey{ - Curve: elliptic.P256(), - X: ephPubX, - Y: ephPubY, + ephPub, err := ecdh.P256().NewPublicKey(ciphertext) + if err != nil { + return nil, fmt.Errorf("couldn't unmarshal ephemeral key: %v", err) } - // marshal, encode, and return the result - shared, err := k.SharedKey(&ephPub) + // perform scalar multiplication, encode, and return the result + shared, err := k.ECDH(ephPub) if err != nil { return nil, fmt.Errorf("couldn't generate shared secret: %v", err) } diff --git a/internal/keyservice/piv/keyservice.go b/internal/keyservice/piv/keyservice.go index 5b4b78e..eb7085c 100644 --- a/internal/keyservice/piv/keyservice.go +++ b/internal/keyservice/piv/keyservice.go @@ -8,7 +8,7 @@ import ( "fmt" "sync" - pivgo "github.com/go-piv/piv-go/piv" + pivgo "github.com/go-piv/piv-go/v2/piv" "github.com/smlx/piv-agent/internal/keyservice/gpg" "github.com/smlx/piv-agent/internal/pinentry" "go.uber.org/zap" diff --git a/internal/keyservice/piv/list.go b/internal/keyservice/piv/list.go index d745b52..a5fba0d 100644 --- a/internal/keyservice/piv/list.go +++ b/internal/keyservice/piv/list.go @@ -7,7 +7,7 @@ import ( "crypto/x509" "fmt" - "github.com/go-piv/piv-go/piv" + pivgo "github.com/go-piv/piv-go/v2/piv" "github.com/smlx/piv-agent/internal/pinentry" "github.com/smlx/piv-agent/internal/securitykey" "go.uber.org/zap" @@ -35,7 +35,7 @@ func (p *KeyService) reloadSecurityKeys() error { } p.securityKeys = nil // open cards and load keys from scratch - cards, err := piv.Cards() + cards, err := pivgo.Cards() if err != nil { return fmt.Errorf("couldn't get cards: %v", err) } diff --git a/internal/mock/mock_assuan.go b/internal/mock/mock_assuan.go index 6b8ae2d..9789c73 100644 --- a/internal/mock/mock_assuan.go +++ b/internal/mock/mock_assuan.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: assuan.go +// +// Generated by this command: +// +// mockgen -source=assuan.go -destination=../mock/mock_assuan.go -package=mock +// // Package mock is a generated GoMock package. package mock @@ -8,7 +13,7 @@ import ( crypto "crypto" reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockKeyService is a mock of KeyService interface. @@ -44,7 +49,7 @@ func (m *MockKeyService) GetDecrypter(arg0 []byte) (crypto.Decrypter, error) { } // GetDecrypter indicates an expected call of GetDecrypter. -func (mr *MockKeyServiceMockRecorder) GetDecrypter(arg0 interface{}) *gomock.Call { +func (mr *MockKeyServiceMockRecorder) GetDecrypter(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDecrypter", reflect.TypeOf((*MockKeyService)(nil).GetDecrypter), arg0) } @@ -59,7 +64,7 @@ func (m *MockKeyService) GetSigner(arg0 []byte) (crypto.Signer, error) { } // GetSigner indicates an expected call of GetSigner. -func (mr *MockKeyServiceMockRecorder) GetSigner(arg0 interface{}) *gomock.Call { +func (mr *MockKeyServiceMockRecorder) GetSigner(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSigner", reflect.TypeOf((*MockKeyService)(nil).GetSigner), arg0) } @@ -75,7 +80,7 @@ func (m *MockKeyService) HaveKey(arg0 [][]byte) (bool, []byte, error) { } // HaveKey indicates an expected call of HaveKey. -func (mr *MockKeyServiceMockRecorder) HaveKey(arg0 interface{}) *gomock.Call { +func (mr *MockKeyServiceMockRecorder) HaveKey(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HaveKey", reflect.TypeOf((*MockKeyService)(nil).HaveKey), arg0) } diff --git a/internal/mock/mock_keyservice.go b/internal/mock/mock_keyservice.go index 83db0af..319a83f 100644 --- a/internal/mock/mock_keyservice.go +++ b/internal/mock/mock_keyservice.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: keyservice.go +// +// Generated by this command: +// +// mockgen -source=keyservice.go -destination=../../mock/mock_keyservice.go -package=mock +// // Package mock is a generated GoMock package. package mock @@ -7,7 +12,7 @@ package mock import ( reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockPINEntryService is a mock of PINEntryService interface. @@ -43,7 +48,7 @@ func (m *MockPINEntryService) GetPassphrase(arg0, arg1 string, arg2 int) ([]byte } // GetPassphrase indicates an expected call of GetPassphrase. -func (mr *MockPINEntryServiceMockRecorder) GetPassphrase(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockPINEntryServiceMockRecorder) GetPassphrase(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPassphrase", reflect.TypeOf((*MockPINEntryService)(nil).GetPassphrase), arg0, arg1, arg2) } diff --git a/internal/mock/mock_pivservice.go b/internal/mock/mock_pivservice.go index a2e404f..1f7ca59 100644 --- a/internal/mock/mock_pivservice.go +++ b/internal/mock/mock_pivservice.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: list.go +// +// Generated by this command: +// +// mockgen -source=list.go -destination=../../mock/mock_pivservice.go -package=mock +// // Package mock is a generated GoMock package. package mock @@ -9,8 +14,8 @@ import ( x509 "crypto/x509" reflect "reflect" - gomock "github.com/golang/mock/gomock" securitykey "github.com/smlx/piv-agent/internal/securitykey" + gomock "go.uber.org/mock/gomock" ) // MockSecurityKey is a mock of SecurityKey interface. @@ -88,7 +93,7 @@ func (m *MockSecurityKey) Comment(arg0 *securitykey.SlotSpec) string { } // Comment indicates an expected call of Comment. -func (mr *MockSecurityKeyMockRecorder) Comment(arg0 interface{}) *gomock.Call { +func (mr *MockSecurityKeyMockRecorder) Comment(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Comment", reflect.TypeOf((*MockSecurityKey)(nil).Comment), arg0) } @@ -117,7 +122,7 @@ func (m *MockSecurityKey) PrivateKey(arg0 *securitykey.CryptoKey) (crypto.Privat } // PrivateKey indicates an expected call of PrivateKey. -func (mr *MockSecurityKeyMockRecorder) PrivateKey(arg0 interface{}) *gomock.Call { +func (mr *MockSecurityKeyMockRecorder) PrivateKey(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrivateKey", reflect.TypeOf((*MockSecurityKey)(nil).PrivateKey), arg0) } @@ -146,7 +151,7 @@ func (m *MockSecurityKey) StringsGPG(arg0, arg1 string) ([]string, error) { } // StringsGPG indicates an expected call of StringsGPG. -func (mr *MockSecurityKeyMockRecorder) StringsGPG(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockSecurityKeyMockRecorder) StringsGPG(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StringsGPG", reflect.TypeOf((*MockSecurityKey)(nil).StringsGPG), arg0, arg1) } diff --git a/internal/securitykey/decryptingkey.go b/internal/securitykey/decryptingkey.go index b62f3cf..b22ad00 100644 --- a/internal/securitykey/decryptingkey.go +++ b/internal/securitykey/decryptingkey.go @@ -6,7 +6,7 @@ import ( "fmt" "github.com/ProtonMail/go-crypto/openpgp/packet" - "github.com/go-piv/piv-go/piv" + pivgo "github.com/go-piv/piv-go/v2/piv" ) // DecryptingKey is a cryptographic decrypting key on a hardware security @@ -17,12 +17,12 @@ type DecryptingKey struct { } // decryptingKeys returns the decrypting keys available on the given yubikey. -func decryptingKeys(yk *piv.YubiKey) ([]DecryptingKey, error) { +func decryptingKeys(yk *pivgo.YubiKey) ([]DecryptingKey, error) { var decryptingKeys []DecryptingKey for _, s := range defaultDecryptSlots { cert, err := yk.Certificate(s.Slot) if err != nil { - if errors.Is(err, piv.ErrNotFound) { + if errors.Is(err, pivgo.ErrNotFound) { continue } return nil, fmt.Errorf("couldn't get certificate for slot %x: %v", diff --git a/internal/securitykey/securitykey.go b/internal/securitykey/securitykey.go index f7d57e8..04f8f0f 100644 --- a/internal/securitykey/securitykey.go +++ b/internal/securitykey/securitykey.go @@ -7,17 +7,17 @@ import ( "crypto/x509" "fmt" - "github.com/go-piv/piv-go/piv" + pivgo "github.com/go-piv/piv-go/v2/piv" "github.com/smlx/piv-agent/internal/pinentry" ) // A SecurityKey is a physical hardware token which implements PIV, such as a // Yubikey. It provides a convenient abstraction around the low-level -// piv.YubiKey object. +// pivgo.YubiKey object. type SecurityKey struct { card string serial uint32 - yubikey *piv.YubiKey + yubikey *pivgo.YubiKey signingKeys []SigningKey decryptingKeys []DecryptingKey cryptoKeys []CryptoKey @@ -32,7 +32,7 @@ type CryptoKey struct { // New returns a security key identified by card string. func New(card string, pe *pinentry.PINEntry) (*SecurityKey, error) { - yk, err := piv.Open(card) + yk, err := pivgo.Open(card) if err != nil { return nil, fmt.Errorf(`couldn't open card "%s": %v`, card, err) } @@ -102,7 +102,7 @@ func (k *SecurityKey) CryptoKeys() []CryptoKey { // PrivateKey returns the private key of the given public signing key. func (k *SecurityKey) PrivateKey(c *CryptoKey) (crypto.PrivateKey, error) { return k.yubikey.PrivateKey(c.SlotSpec.Slot, c.Public, - piv.KeyAuth{PINPrompt: k.pinentry.GetPin(k)}) + pivgo.KeyAuth{PINPrompt: k.pinentry.GetPin(k)}) } // Close closes the underlying yubikey. diff --git a/internal/securitykey/setup.go b/internal/securitykey/setup.go index aff3cdf..2eac854 100644 --- a/internal/securitykey/setup.go +++ b/internal/securitykey/setup.go @@ -11,7 +11,7 @@ import ( "math/big" "time" - "github.com/go-piv/piv-go/piv" + pivgo "github.com/go-piv/piv-go/v2/piv" ) // ErrKeySetUp is returned from Setup when the security key is already set up @@ -24,7 +24,7 @@ func (k *SecurityKey) checkSlotSetUp(s SlotSpec) (bool, error) { _, err := k.yubikey.Certificate(s.Slot) if err == nil { return true, nil - } else if errors.Is(err, piv.ErrNotFound) { + } else if errors.Is(err, pivgo.ErrNotFound) { return false, nil } return false, fmt.Errorf("couldn't check slot certificate: %v", err) @@ -73,23 +73,23 @@ func (k *SecurityKey) Setup(pin, version string, reset bool, return fmt.Errorf("couldn't reset security key: %v", err) } // generate management key and store on the security key - var mgmtKey [24]byte - if _, err := rand.Read(mgmtKey[:]); err != nil { + var mgmtKey = make([]byte, 24) + if _, err := rand.Read(mgmtKey); err != nil { return fmt.Errorf("couldn't get random bytes: %v", err) } - err = k.yubikey.SetManagementKey(piv.DefaultManagementKey, mgmtKey) + err = k.yubikey.SetManagementKey(pivgo.DefaultManagementKey, mgmtKey) if err != nil { return fmt.Errorf("couldn't set management key: %v", err) } - err = k.yubikey.SetMetadata(mgmtKey, &piv.Metadata{ManagementKey: &mgmtKey}) + err = k.yubikey.SetMetadata(mgmtKey, &pivgo.Metadata{ManagementKey: &mgmtKey}) if err != nil { return fmt.Errorf("couldn't store management key: %v", err) } // set pin/puk - if err = k.yubikey.SetPIN(piv.DefaultPIN, pin); err != nil { + if err = k.yubikey.SetPIN(pivgo.DefaultPIN, pin); err != nil { return fmt.Errorf("couldn't set PIN: %v", err) } - if err = k.yubikey.SetPUK(piv.DefaultPUK, pin); err != nil { + if err = k.yubikey.SetPUK(pivgo.DefaultPUK, pin); err != nil { return fmt.Errorf("couldn't set PUK: %v", err) } // setup signing keys @@ -111,11 +111,11 @@ func (k *SecurityKey) Setup(pin, version string, reset bool, return nil } -func (k *SecurityKey) configureSlot(mgmtKey [24]byte, spec SlotSpec, +func (k *SecurityKey) configureSlot(mgmtKey []byte, spec SlotSpec, version string) error { - pub, err := k.yubikey.GenerateKey(mgmtKey, spec.Slot, piv.Key{ - Algorithm: piv.AlgorithmEC256, - PINPolicy: piv.PINPolicyOnce, + pub, err := k.yubikey.GenerateKey(mgmtKey, spec.Slot, pivgo.Key{ + Algorithm: pivgo.AlgorithmEC256, + PINPolicy: pivgo.PINPolicyOnce, TouchPolicy: spec.TouchPolicy, }) if err != nil { diff --git a/internal/securitykey/signingkey.go b/internal/securitykey/signingkey.go index c8f4819..c72b8a6 100644 --- a/internal/securitykey/signingkey.go +++ b/internal/securitykey/signingkey.go @@ -6,7 +6,7 @@ import ( "fmt" "github.com/ProtonMail/go-crypto/openpgp/packet" - "github.com/go-piv/piv-go/piv" + pivgo "github.com/go-piv/piv-go/v2/piv" "golang.org/x/crypto/ssh" ) @@ -18,12 +18,12 @@ type SigningKey struct { } // signingKeys returns the signing keys available on the given yubikey. -func signingKeys(yk *piv.YubiKey) ([]SigningKey, error) { +func signingKeys(yk *pivgo.YubiKey) ([]SigningKey, error) { var signingKeys []SigningKey for _, s := range defaultSignSlots { cert, err := yk.Certificate(s.Slot) if err != nil { - if errors.Is(err, piv.ErrNotFound) { + if errors.Is(err, pivgo.ErrNotFound) { continue } return nil, fmt.Errorf("couldn't get certificate for slot %x: %v", diff --git a/internal/securitykey/slotspec.go b/internal/securitykey/slotspec.go index ebe7423..9a94bc7 100644 --- a/internal/securitykey/slotspec.go +++ b/internal/securitykey/slotspec.go @@ -1,11 +1,11 @@ package securitykey -import "github.com/go-piv/piv-go/piv" +import pivgo "github.com/go-piv/piv-go/v2/piv" // SlotSpec represents a combination of slot and touch policy on the token. type SlotSpec struct { - Slot piv.Slot - TouchPolicy piv.TouchPolicy + Slot pivgo.Slot + TouchPolicy pivgo.TouchPolicy } // defaultSignSlots represents the default slot specifications for signing @@ -18,7 +18,7 @@ var defaultSignSlots = map[string]SlotSpec{ // login. The end user PIN is required to perform any private key operations. // Once the PIN has been provided successfully, multiple private key // operations may be performed without additional cardholder consent. - "cached": {piv.SlotAuthentication, piv.TouchPolicyCached}, + "cached": {pivgo.SlotAuthentication, pivgo.TouchPolicyCached}, // Slot 9c: Digital Signature // This certificate and its associated private key is used for digital // signatures for the purpose of document signing, or signing files and @@ -26,17 +26,17 @@ var defaultSignSlots = map[string]SlotSpec{ // operations. The PIN must be submitted every time immediately before a sign // operation, to ensure cardholder participation for every digital signature // generated. - "always": {piv.SlotSignature, piv.TouchPolicyAlways}, + "always": {pivgo.SlotSignature, pivgo.TouchPolicyAlways}, // Slot 9e: Card Authentication // This certificate and its associated private key is used to support // additional physical access applications, such as providing physical access // to buildings via PIV-enabled door locks. The end user PIN is NOT required // to perform private key operations for this slot. - "never": {piv.SlotCardAuthentication, piv.TouchPolicyNever}, + "never": {pivgo.SlotCardAuthentication, pivgo.TouchPolicyNever}, } -var alwaysDecryptSlot, _ = piv.RetiredKeyManagementSlot(0x82) -var neverDecryptSlot, _ = piv.RetiredKeyManagementSlot(0x83) +var alwaysDecryptSlot, _ = pivgo.RetiredKeyManagementSlot(0x82) +var neverDecryptSlot, _ = pivgo.RetiredKeyManagementSlot(0x83) // defaultDecryptSlots represents the slot specifications for decrypting // operations. By using additional "retired" slots we can enable multiple touch @@ -49,9 +49,9 @@ var defaultDecryptSlots = map[string]SlotSpec{ // private key operations. Once the PIN has been provided successfully, // multiple private key operations may be performed without additional // cardholder consent. - "cached": {piv.SlotKeyManagement, piv.TouchPolicyCached}, + "cached": {pivgo.SlotKeyManagement, pivgo.TouchPolicyCached}, // "Retired" key management slot with an "always" touch policy. - "always": {alwaysDecryptSlot, piv.TouchPolicyAlways}, + "always": {alwaysDecryptSlot, pivgo.TouchPolicyAlways}, // "Retired" key management slot with a "never" touch policy. - "never": {neverDecryptSlot, piv.TouchPolicyNever}, + "never": {neverDecryptSlot, pivgo.TouchPolicyNever}, } diff --git a/internal/securitykey/string.go b/internal/securitykey/string.go index c7ee1c6..49258ee 100644 --- a/internal/securitykey/string.go +++ b/internal/securitykey/string.go @@ -13,14 +13,14 @@ import ( openpgpecdsa "github.com/ProtonMail/go-crypto/openpgp/ecdsa" "github.com/ProtonMail/go-crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/packet" - "github.com/go-piv/piv-go/piv" + pivgo "github.com/go-piv/piv-go/v2/piv" "golang.org/x/crypto/ssh" ) -var touchStringMap = map[piv.TouchPolicy]string{ - piv.TouchPolicyNever: "never", - piv.TouchPolicyAlways: "always", - piv.TouchPolicyCached: "cached", +var touchStringMap = map[pivgo.TouchPolicy]string{ + pivgo.TouchPolicyNever: "never", + pivgo.TouchPolicyAlways: "always", + pivgo.TouchPolicyCached: "cached", } // Entity wraps a synthesized openpgp.Entity and associates it with a @@ -127,7 +127,7 @@ func (k *SecurityKey) synthesizeEntities(name, email string) ([]Entity, } func (k *SecurityKey) armorEntity(e *openpgp.Entity, - t piv.TouchPolicy) (string, error) { + t pivgo.TouchPolicy) (string, error) { buf := bytes.Buffer{} w, err := armor.Encode(&buf, openpgp.PublicKeyType, map[string]string{