From 453e81905b3a00fceeaad9fbed0d5af1f0099978 Mon Sep 17 00:00:00 2001 From: Lukas Burkhalter <10532077+lubux@users.noreply.github.com> Date: Wed, 28 Feb 2024 13:51:52 +0100 Subject: [PATCH] Add support for the crypto refresh in v2 (#265) This commit adds support for the OpenPGP crypto-refresh. - Updates go-crypto dependency to v1.1.0-alpha.1 - Adapts the session key logic to handle PKESK/SKESK v6 packets without an algorithm attached - Updates the min go version to 1.17 as requires by go-crypto v1.1.0-alpha.1 - Update the cricl dependency to 1.3.7 matching go-crypto Not supported: - crypto-refresh intended recipients - v6 key generation --- .github/test-suite/config.json.template | 4 +++ .github/test-suite/prepare_config.sh | 1 + .github/workflows/go.yml | 6 ++--- .github/workflows/sop-test-suite.yml | 4 +-- crypto/key_clear.go | 36 +++++++++++++++++++++++++ crypto/password.go | 1 + crypto/sessionkey.go | 17 +++++++++++- go.mod | 16 ++++++++--- go.sum | 36 ++++++++++++------------- 9 files changed, 92 insertions(+), 29 deletions(-) diff --git a/.github/test-suite/config.json.template b/.github/test-suite/config.json.template index ac3701f..2e49197 100644 --- a/.github/test-suite/config.json.template +++ b/.github/test-suite/config.json.template @@ -17,6 +17,10 @@ { "path": "__SOP_OPENPGPJS__" }, + { + "id": "gosop-v2", + "path": "__GOSOP_V2__" + }, { "path": "__RNP_SOP__" } diff --git a/.github/test-suite/prepare_config.sh b/.github/test-suite/prepare_config.sh index ae49d01..ca3feef 100755 --- a/.github/test-suite/prepare_config.sh +++ b/.github/test-suite/prepare_config.sh @@ -8,5 +8,6 @@ cat $CONFIG_TEMPLATE \ | sed "s@__SQOP__@${SQOP}@g" \ | sed "s@__GPGME_SOP__@${GPGME_SOP}@g" \ | sed "s@__SOP_OPENPGPJS__@${SOP_OPENPGPJS}@g" \ + | sed "s@__GOSOP_V2__@${GOSOP_DIR_V2}/gosop@g" \ | sed "s@__RNP_SOP__@${RNP_SOP}@g" \ > $CONFIG_OUTPUT \ No newline at end of file diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 7d2bff8..8f94532 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -23,16 +23,16 @@ jobs: run: go test -v -race ./... test-old: - name: Test with 1.15 + name: Test with 1.17 runs-on: ubuntu-latest steps: - name: Check out repo uses: actions/checkout@v3 - - name: Set up Go 1.15 + - name: Set up Go 1.17 uses: actions/setup-go@v3 with: - go-version: 1.15 + go-version: 1.17 - name: Test run: go test -v -race ./... diff --git a/.github/workflows/sop-test-suite.yml b/.github/workflows/sop-test-suite.yml index 84959ca..ca76182 100644 --- a/.github/workflows/sop-test-suite.yml +++ b/.github/workflows/sop-test-suite.yml @@ -46,7 +46,7 @@ jobs: name: Run interoperability test suite runs-on: ubuntu-latest container: - image: ghcr.io/protonmail/openpgp-interop-test-docker:v1.1.1 + image: ghcr.io/protonmail/openpgp-interop-test-docker:v1.1.4 credentials: username: ${{ github.actor }} password: ${{ secrets.github_token }} @@ -117,7 +117,7 @@ jobs: with: name: test-suite-results.json - name: Compare with baseline - uses: ProtonMail/openpgp-interop-test-analyzer@v1 + uses: ProtonMail/openpgp-interop-test-analyzer@5d7f4b6868ebe3bfc909302828342c461f5f4940 with: results: ${{ steps.download-test-results.outputs.download-path }}/test-suite-results.json output: baseline-comparison.json diff --git a/crypto/key_clear.go b/crypto/key_clear.go index c628f36..e46eb89 100644 --- a/crypto/key_clear.go +++ b/crypto/key_clear.go @@ -8,8 +8,12 @@ import ( "github.com/ProtonMail/go-crypto/openpgp/ecdh" "github.com/ProtonMail/go-crypto/openpgp/ecdsa" + "github.com/ProtonMail/go-crypto/openpgp/ed25519" + "github.com/ProtonMail/go-crypto/openpgp/ed448" "github.com/ProtonMail/go-crypto/openpgp/eddsa" "github.com/ProtonMail/go-crypto/openpgp/elgamal" + "github.com/ProtonMail/go-crypto/openpgp/x25519" + "github.com/ProtonMail/go-crypto/openpgp/x448" ) func (sk *SessionKey) Clear() (ok bool) { @@ -61,6 +65,14 @@ func clearPrivateKey(privateKey interface{}) error { return clearEdDSAPrivateKey(priv) case *ecdh.PrivateKey: return clearECDHPrivateKey(priv) + case *x25519.PrivateKey: + return clearX25519PrivateKey(priv) + case *ed25519.PrivateKey: + return clearEd25519PrivateKey(priv) + case *x448.PrivateKey: + return clearX448PrivateKey(priv) + case *ed448.PrivateKey: + return clearEd448PrivateKey(priv) default: return errors.New("gopenpgp: unknown private key") } @@ -126,3 +138,27 @@ func clearECDHPrivateKey(priv *ecdh.PrivateKey) error { return nil } + +func clearX25519PrivateKey(priv *x25519.PrivateKey) error { + clearMem(priv.Secret) + + return nil +} + +func clearEd25519PrivateKey(priv *ed25519.PrivateKey) error { + clearMem(priv.Key[:ed25519.SeedSize]) + + return nil +} + +func clearX448PrivateKey(priv *x448.PrivateKey) error { + clearMem(priv.Secret) + + return nil +} + +func clearEd448PrivateKey(priv *ed448.PrivateKey) error { + clearMem(priv.Key[:ed448.SeedSize]) + + return nil +} diff --git a/crypto/password.go b/crypto/password.go index 64ee026..40ebad9 100644 --- a/crypto/password.go +++ b/crypto/password.go @@ -57,6 +57,7 @@ func DecryptSessionKeyWithPassword(keyPacket, password []byte) (*SessionKey, err key, cipherFunc, err := s.Decrypt(password) if err == nil { sk := &SessionKey{ + V6: s.Version == 6, Key: key, Algo: getAlgo(cipherFunc), } diff --git a/crypto/sessionkey.go b/crypto/sessionkey.go index a523fce..6211bad 100644 --- a/crypto/sessionkey.go +++ b/crypto/sessionkey.go @@ -17,6 +17,7 @@ import ( // SessionKey stores a decrypted session key. type SessionKey struct { + V6 bool // The decrypted binary session key. Key []byte // The symmetric encryption algorithm used with this key. @@ -57,6 +58,9 @@ func (cr checkReader) Read(buf []byte) (int, error) { // GetCipherFunc returns the cipher function corresponding to the algorithm used // with this SessionKey. func (sk *SessionKey) GetCipherFunc() (packet.CipherFunction, error) { + if sk.V6 { + return 0, nil + } cf, ok := symKeyAlgos[sk.Algo] if !ok { return cf, errors.New("gopenpgp: unsupported cipher function: " + sk.Algo) @@ -107,6 +111,7 @@ func NewSessionKeyFromToken(token []byte, algo string) *SessionKey { return &SessionKey{ Key: clone(token), Algo: algo, + V6: algo == "", } } @@ -118,13 +123,14 @@ func newSessionKeyFromEncrypted(ek *packet.EncryptedKey) (*SessionKey, error) { break } } - if algo == "" { + if algo == "" && ek.Version < 6 { return nil, fmt.Errorf("gopenpgp: unsupported cipher function: %v", ek.CipherFunc) } sk := &SessionKey{ Key: ek.Key, Algo: algo, + V6: ek.Version == 6, } if err := sk.checkSize(); err != nil { @@ -455,6 +461,12 @@ func decryptStreamWithSessionKey( } func (sk *SessionKey) checkSize() error { + if sk.V6 { + if len(sk.Key) == 0 { + return errors.New("empty session key") + } + return nil + } cf, ok := symKeyAlgos[sk.Algo] if !ok { return errors.New("unknown symmetric key algorithm") @@ -468,6 +480,9 @@ func (sk *SessionKey) checkSize() error { } func getAlgo(cipher packet.CipherFunction) string { + if cipher == 0 { + return "" + } algo := constants.AES256 for k, v := range symKeyAlgos { if v == cipher { diff --git a/go.mod b/go.mod index fdd3515..8d21b6c 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,20 @@ module github.com/ProtonMail/gopenpgp/v2 -go 1.15 +go 1.17 require ( - github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 + github.com/ProtonMail/go-crypto v1.1.0-alpha.1 github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f - github.com/davecgh/go-spew v1.1.1 // indirect github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 - golang.org/x/crypto v0.7.0 + golang.org/x/crypto v0.17.0 +) + +require ( + github.com/cloudflare/circl v1.3.7 // indirect + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/go.sum b/go.sum index 89bf4e7..260c580 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,12 @@ -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v1.1.0-alpha.1 h1:iKLDnKGL+3u4Q5OjYgixAxWdkkGBPidCQumqVryUgtY= +github.com/ProtonMail/go-crypto v1.1.0-alpha.1/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +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/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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -18,17 +17,15 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 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-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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 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/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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= @@ -37,23 +34,24 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 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.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=