From 0f64e67ff02d0ad28b6b97ec5665316bfaa206c7 Mon Sep 17 00:00:00 2001 From: Aron Wussler Date: Fri, 17 Mar 2023 10:15:04 +0100 Subject: [PATCH 1/4] Switch go-crypto to proton branch and add tests for forwarding and symmetric keys --- crypto/proton_test.go | 68 +++++++++++++++++++++++++++++++ crypto/testdata/key_forwardee | 15 +++++++ crypto/testdata/key_symmetric | 15 +++++++ crypto/testdata/message_forwardee | 8 ++++ go.mod | 2 +- go.sum | 4 +- 6 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 crypto/proton_test.go create mode 100644 crypto/testdata/key_forwardee create mode 100644 crypto/testdata/key_symmetric create mode 100644 crypto/testdata/message_forwardee diff --git a/crypto/proton_test.go b/crypto/proton_test.go new file mode 100644 index 00000000..12d20c9c --- /dev/null +++ b/crypto/proton_test.go @@ -0,0 +1,68 @@ +package crypto + +import ( + "encoding/base64" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestForwardeeDecryption(t *testing.T) { + pgp.latestServerTime = 1679044110 + defer func() { + pgp.latestServerTime = testTime + }() + + forwardeeKey, err := NewKeyFromArmored(readTestFile("key_forwardee", false)) + if err != nil { + t.Fatal("Expected no error while unarmoring private keyring, got:", err) + } + + forwardeeKeyRing, err := NewKeyRing(forwardeeKey) + if err != nil { + t.Fatal("Expected no error while building private keyring, got:", err) + } + + pgpMessage, err := NewPGPMessageFromArmored(readTestFile("message_forwardee", false)) + if err != nil { + t.Fatal("Expected no error while reading ciphertext, got:", err) + } + + plainMessage, err := forwardeeKeyRing.Decrypt(pgpMessage, nil, 0) + if err != nil { + t.Fatal("Expected no error while decrypting/verifying, got:", err) + } + + assert.Exactly(t, "Message for Bob", plainMessage.GetString()) +} + +func TestSymmetricKeys(t *testing.T) { + pgp.latestServerTime = 1679044110 + defer func() { + pgp.latestServerTime = testTime + }() + + symmetricKey, err := NewKeyFromArmored(readTestFile("key_symmetric", false)) + if err != nil { + t.Fatal("Expected no error while unarmoring private keyring, got:", err) + } + + symmetricKeyRing, err := NewKeyRing(symmetricKey) + if err != nil { + t.Fatal("Expected no error while building private keyring, got:", err) + } + + binData, _ := base64.StdEncoding.DecodeString("ExXmnSiQ2QCey20YLH6qlLhkY3xnIBC1AwlIXwK/HvY=") + var message = NewPlainMessage(binData) + + ciphertext, err := symmetricKeyRing.Encrypt(message, nil) + if err != nil { + t.Fatal("Expected no error when encrypting, got:", err) + } + + decrypted, err := symmetricKeyRing.Decrypt(ciphertext, nil, 0) + if err != nil { + t.Fatal("Expected no error when decrypting, got:", err) + } + assert.Exactly(t, message.GetBinary(), decrypted.GetBinary()) +} diff --git a/crypto/testdata/key_forwardee b/crypto/testdata/key_forwardee new file mode 100644 index 00000000..adf82ad1 --- /dev/null +++ b/crypto/testdata/key_forwardee @@ -0,0 +1,15 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xVgEZAdtGBYJKwYBBAHaRw8BAQdAcNgHyRGEaqGmzEqEwCobfUkyrJnY8faBvsf9 +R2c5ZzYAAP9bFL4nPBdo04ei0C2IAh5RXOpmuejGC3GAIn/UmL5cYQ+XzRtjaGFy +bGVzIDxjaGFybGVzQHByb3Rvbi5tZT7CigQTFggAPAUCZAdtGAmQFXJtmBzDhdcW +IQRl2gNflypl1XjRUV8Vcm2YHMOF1wIbAwIeAQIZAQILBwIVCAIWAAIiAQAAJKYA +/2qY16Ozyo5erNz51UrKViEoWbEpwY3XaFVNzrw+b54YAQC7zXkf/t5ieylvjmA/ +LJz3/qgH5GxZRYAH9NTpWyW1AsdxBGQHbRgSCisGAQQBl1UBBQEBB0CxmxoJsHTW +TiETWh47ot+kwNA1hCk1IYB9WwKxkXYyIBf/CgmKXzV1ODP/mRmtiBYVV+VQk5MF +EAAA/1NW8D8nMc2ky140sPhQrwkeR7rVLKP2fe5n4BEtAnVQEB3CeAQYFggAKgUC +ZAdtGAmQFXJtmBzDhdcWIQRl2gNflypl1XjRUV8Vcm2YHMOF1wIbUAAAl/8A/iIS +zWBsBR8VnoOVfEE+VQk6YAi7cTSjcMjfsIez9FYtAQDKo9aCMhUohYyqvhZjn8aS +3t9mIZPc+zRJtCHzQYmhDg== +=lESj +-----END PGP PRIVATE KEY BLOCK----- \ No newline at end of file diff --git a/crypto/testdata/key_symmetric b/crypto/testdata/key_symmetric new file mode 100644 index 00000000..98f702f2 --- /dev/null +++ b/crypto/testdata/key_symmetric @@ -0,0 +1,15 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xVgEYs/4KxYJKwYBBAHaRw8BAQdA7tIsntXluwloh/H62PJMqasjP00M86fv +/Pof9A968q8AAQDYcgkPKUdWAxsDjDHJfouPS4q5Me3ks+umlo5RJdwLZw4k +zQ1TeW1tZXRyaWMgS2V5wowEEBYKAB0FAmLP+CsECwkHCAMVCAoEFgACAQIZ +AQIbAwIeAQAhCRDkNhFDvaU8vxYhBDJNoyEFquVOCf99d+Q2EUO9pTy/5XQA +/1F2YPouv0ydBDJU3EOS/4bmPt7yqvzciWzeKVEOkzYuAP9OsP7q/5ccqOPX +mmRUKwd82/cNjdzdnWZ8Tq89XMwMAMdqBGLP+CtkCfFyZxOMF0BWLwAE8pLy +RVj2n2K7k6VvrhyuTqDkFDUFALiSLrEfnmTKlsPYS3/YzsODF354ccR63q73 +3lmCrvFRyaf6AHvVrBYPbJR+VhuTjZTwZKvPPKv0zVdSqi5JDEQiocJ4BBgW +CAAJBQJiz/grAhsMACEJEOQ2EUO9pTy/FiEEMk2jIQWq5U4J/3135DYRQ72l +PL+fEQEA7RaRbfa+AtiRN7a4GuqVEDZi3qtQZ2/Qcb27/LkAD0sA/3r9drYv +jyu46h1fdHHyo0HS2MiShZDZ8u60JnDltloD +=8TxH +-----END PGP PRIVATE KEY BLOCK----- \ No newline at end of file diff --git a/crypto/testdata/message_forwardee b/crypto/testdata/message_forwardee new file mode 100644 index 00000000..cd44ecf1 --- /dev/null +++ b/crypto/testdata/message_forwardee @@ -0,0 +1,8 @@ +-----BEGIN PGP MESSAGE----- + +wV4DB27Wn97eACkSAQdA62TlMU2QoGmf5iBLnIm4dlFRkLIg+6MbaatghwxK+Ccw +yGZuVVMAK/ypFfebDf4D/rlEw3cysv213m8aoK8nAUO8xQX3XQq3Sg+EGm0BNV8E +0kABEPyCWARoo5klT1rHPEhelnz8+RQXiOIX3G685XCWdCmaV+tzW082D0xGXSlC +7lM8r1DumNnO8srssko2qIja +=pVRa +-----END PGP MESSAGE----- \ No newline at end of file diff --git a/go.mod b/go.mod index 13c9a0a9..0a426b08 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/ProtonMail/gopenpgp/v3 go 1.17 require ( - github.com/ProtonMail/go-crypto v1.1.0-beta.0 + github.com/ProtonMail/go-crypto v1.1.0-beta.0-proton github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index 8ef82513..f72d8b4e 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/ProtonMail/go-crypto v1.1.0-beta.0 h1:9ZLo7gzqEbrSakeRM4L0jaHMuZSLrjoYBIdIwcBr4C4= -github.com/ProtonMail/go-crypto v1.1.0-beta.0/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.1.0-beta.0-proton h1:ZGewsAoeSirbUS5cO8L0FMQA+iSop9xR1nmFYifDBPo= +github.com/ProtonMail/go-crypto v1.1.0-beta.0-proton/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= From 42bd89259a566653536afb7ff13a499a61acd758 Mon Sep 17 00:00:00 2001 From: Lukas Burkhalter Date: Tue, 26 Sep 2023 11:16:36 +0200 Subject: [PATCH 2/4] Adapt to gopenpgp v3 --- crypto/proton_test.go | 45 ++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/crypto/proton_test.go b/crypto/proton_test.go index 12d20c9c..f608f5df 100644 --- a/crypto/proton_test.go +++ b/crypto/proton_test.go @@ -8,10 +8,7 @@ import ( ) func TestForwardeeDecryption(t *testing.T) { - pgp.latestServerTime = 1679044110 - defer func() { - pgp.latestServerTime = testTime - }() + //pgp.latestServerTime = 1679044110 forwardeeKey, err := NewKeyFromArmored(readTestFile("key_forwardee", false)) if err != nil { @@ -23,25 +20,23 @@ func TestForwardeeDecryption(t *testing.T) { t.Fatal("Expected no error while building private keyring, got:", err) } - pgpMessage, err := NewPGPMessageFromArmored(readTestFile("message_forwardee", false)) + pgpMessage := readTestFile("message_forwardee", false) + decryptor, err := PGP().Decryption(). + DecryptionKeys(forwardeeKeyRing). + VerifyTime(1679044110). + New() if err != nil { - t.Fatal("Expected no error while reading ciphertext, got:", err) + t.Fatal(err) } - - plainMessage, err := forwardeeKeyRing.Decrypt(pgpMessage, nil, 0) + plainMessage, err := decryptor.Decrypt([]byte(pgpMessage), Armor) if err != nil { t.Fatal("Expected no error while decrypting/verifying, got:", err) } - assert.Exactly(t, "Message for Bob", plainMessage.GetString()) + assert.Exactly(t, "Message for Bob", plainMessage.String()) } func TestSymmetricKeys(t *testing.T) { - pgp.latestServerTime = 1679044110 - defer func() { - pgp.latestServerTime = testTime - }() - symmetricKey, err := NewKeyFromArmored(readTestFile("key_symmetric", false)) if err != nil { t.Fatal("Expected no error while unarmoring private keyring, got:", err) @@ -53,16 +48,30 @@ func TestSymmetricKeys(t *testing.T) { } binData, _ := base64.StdEncoding.DecodeString("ExXmnSiQ2QCey20YLH6qlLhkY3xnIBC1AwlIXwK/HvY=") - var message = NewPlainMessage(binData) + pgp := PGP() + encryptor, err := pgp.Encryption(). + Recipients(symmetricKeyRing). + SignTime(1679044110). + New() + if err != nil { + t.Fatal(err) + } - ciphertext, err := symmetricKeyRing.Encrypt(message, nil) + ciphertext, err := encryptor.Encrypt(binData) if err != nil { t.Fatal("Expected no error when encrypting, got:", err) } - decrypted, err := symmetricKeyRing.Decrypt(ciphertext, nil, 0) + decryptor, err := pgp.Decryption(). + DecryptionKeys(symmetricKeyRing). + VerifyTime(1679044110). + New() + if err != nil { + t.Fatal(err) + } + decrypted, err := decryptor.Decrypt(ciphertext.Bytes(), Bytes) if err != nil { t.Fatal("Expected no error when decrypting, got:", err) } - assert.Exactly(t, message.GetBinary(), decrypted.GetBinary()) + assert.Exactly(t, binData, decrypted.Bytes()) } From 78fc4045b9d50c7963d1dd6237e3c27041cd584a Mon Sep 17 00:00:00 2001 From: Lukas Burkhalter Date: Tue, 12 Dec 2023 11:13:09 +0100 Subject: [PATCH 3/4] feat: Add a preset proton profile and replace default --- profile/preset.go | 49 ++++++++++++++++++++++++++++------------------ profile/profile.go | 11 ++++++++++- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/profile/preset.go b/profile/preset.go index ac95f0e6..054e96e2 100644 --- a/profile/preset.go +++ b/profile/preset.go @@ -11,25 +11,7 @@ import ( // Default returns a custom profile that support features // that are widely implemented. func Default() *Custom { - setKeyAlgorithm := func(cfg *packet.Config, securityLevel int8) { - cfg.Algorithm = packet.PubKeyAlgoEdDSA - switch securityLevel { - case constants.HighSecurity: - cfg.Curve = packet.Curve25519 - default: - cfg.Curve = packet.Curve25519 - } - } - return &Custom{ - Name: "default", - SetKeyAlgorithm: setKeyAlgorithm, - Hash: crypto.SHA256, - CipherEncryption: packet.CipherAES256, - CompressionAlgorithm: packet.CompressionZLIB, - CompressionConfiguration: &packet.CompressionConfig{ - Level: 6, - }, - } + return ProtonV1() } // RFC4880 returns a custom profile for this library @@ -83,3 +65,32 @@ func RFC9580() *Custom { V6: true, } } + +// ProtonV1 is the version 1 profile used in proton clients. +func ProtonV1() *Custom { + setKeyAlgorithm := func(cfg *packet.Config, securityLevel int8) { + cfg.Algorithm = packet.PubKeyAlgoEdDSA + switch securityLevel { + case constants.HighSecurity: + cfg.Curve = packet.Curve25519 + default: + cfg.Curve = packet.Curve25519 + } + } + return &Custom{ + Name: "proton-v1", + SetKeyAlgorithm: setKeyAlgorithm, + Hash: crypto.SHA512, + CipherEncryption: packet.CipherAES256, + CompressionAlgorithm: packet.CompressionZLIB, + KeyGenAeadEncryption: &packet.AEADConfig{ + DefaultMode: packet.AEADModeGCM, + }, + CompressionConfiguration: &packet.CompressionConfig{ + Level: 6, + }, + DisableIntendedRecipients: true, + AllowAllPublicKeyAlgorithms: true, + AllowWeakRSA: true, + } +} diff --git a/profile/profile.go b/profile/profile.go index d86930b3..51c3e9b9 100644 --- a/profile/profile.go +++ b/profile/profile.go @@ -26,7 +26,12 @@ type Custom struct { // S2kKeyEncryption defines the s2k algorithm for key encryption. S2kKeyEncryption *s2k.Config // AeadEncryption defines the aead encryption algorithm for pgp encryption. + // If nil, aead is disabled even if the key supports it. AeadEncryption *packet.AEADConfig + // KeyGenAeadEncryption defines if the output key in key generation + // advertises SEIPDv2 and aead algorithms in its key preferences. + // If nil, uses AeadEncryption as key preferences. + KeyGenAeadEncryption *packet.AEADConfig // S2kEncryption defines the s2k algorithm for pgp encryption. S2kEncryption *s2k.Config // CompressionConfiguration defines the compression configuration to be used if any. @@ -56,10 +61,14 @@ type Custom struct { // KeyGenerationProfile, KeyEncryptionProfile, EncryptionProfile, and SignProfile func (p *Custom) KeyGenerationConfig(securityLevel int8) *packet.Config { + aeadConfig := p.AeadEncryption + if p.KeyGenAeadEncryption != nil { + aeadConfig = p.KeyGenAeadEncryption + } cfg := &packet.Config{ DefaultHash: p.Hash, DefaultCipher: p.CipherEncryption, - AEADConfig: p.AeadEncryption, + AEADConfig: aeadConfig, DefaultCompressionAlgo: p.CompressionAlgorithm, CompressionConfig: p.CompressionConfiguration, V6Keys: p.V6, From 83481d9c24bdcfcefbae1e4e9b4e8c7423bbba6d Mon Sep 17 00:00:00 2001 From: Lukas Burkhalter Date: Tue, 25 Jun 2024 17:03:24 +0200 Subject: [PATCH 4/4] feat: Update key encryption in proton profile - Set key encryption cipher to aes-256 - Set s2k count to 65536 --- profile/preset.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/profile/preset.go b/profile/preset.go index 054e96e2..3a51f505 100644 --- a/profile/preset.go +++ b/profile/preset.go @@ -82,6 +82,7 @@ func ProtonV1() *Custom { SetKeyAlgorithm: setKeyAlgorithm, Hash: crypto.SHA512, CipherEncryption: packet.CipherAES256, + CipherKeyEncryption: packet.CipherAES256, CompressionAlgorithm: packet.CompressionZLIB, KeyGenAeadEncryption: &packet.AEADConfig{ DefaultMode: packet.AEADModeGCM, @@ -89,6 +90,11 @@ func ProtonV1() *Custom { CompressionConfiguration: &packet.CompressionConfig{ Level: 6, }, + S2kKeyEncryption: &s2k.Config{ + S2KMode: s2k.IteratedSaltedS2K, + Hash: crypto.SHA256, + S2KCount: 65536, + }, DisableIntendedRecipients: true, AllowAllPublicKeyAlgorithms: true, AllowWeakRSA: true,