aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorMarcin Serwin <marcin@serwin.dev>2024-10-20 00:01:58 +0200
committerRobin Jarry <robin@jarry.cc>2024-10-23 10:22:45 +0200
commit63db443949b92eed5f5d2ded5f430bda96312b43 (patch)
tree3963176515de3fe4ccc8cda41496a5fe672b49ec /lib
parent7469730f044a4c2e929919004ea6971cdced3637 (diff)
downloadaerc-63db443949b92eed5f5d2ded5f430bda96312b43.tar.gz
gpg: do not sign with untrusted keys
During encryption gpg was ignoring trust model of the user in favor of always using the insecure "always". This change removes this override and adds a helpful message if a user tries to encrypt a message with an untrusted key. To revert to the previous behavior users can add "trust-model always" to their gpg.conf file (default ~/.gnupg/gpg.conf). Signed-off-by: Marcin Serwin <marcin@serwin.dev> Acked-by: Tim Culverhouse <tim@timculverhouse.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/crypto/gpg/gpg_test.go57
-rw-r--r--lib/crypto/gpg/gpgbin/encrypt.go5
-rw-r--r--lib/crypto/gpg/gpgbin/gpgbin.go5
-rw-r--r--lib/crypto/gpg/gpgbin/import-ownertrust.go16
-rw-r--r--lib/crypto/gpg/reader_test.go7
-rw-r--r--lib/crypto/gpg/writer_test.go30
6 files changed, 82 insertions, 38 deletions
diff --git a/lib/crypto/gpg/gpg_test.go b/lib/crypto/gpg/gpg_test.go
index 25d73693..2cd0c3dc 100644
--- a/lib/crypto/gpg/gpg_test.go
+++ b/lib/crypto/gpg/gpg_test.go
@@ -126,32 +126,35 @@ hsgCjGTIAOQ5lNwpLEMjwLias6e5sM6hcK9Wo+A9Sw23f8lMau5clOZTJeyAUAff
const testPublicKeyArmored = `-----BEGIN PGP PUBLIC KEY BLOCK-----
-mQENBF5FJf8BCACvlKhSSsv4P8C3Wbv391SrNUBtFquoMuWKtuCr/Ks6KHuofGLn
-bM55uBSQp908aITBDPkaOPsQ3OvwgF7SM8bNIDVpO7FHzCEg2Ysp99iPET/+LsbY
-ugc8oYSuvA5aFFIOMYbAbI+HmbIBuCs+xp0AcU1cemAPzPBDCZs4xl5Y+/ce2yQz
-ZGK9O/tQQIKoBUOWLo/0byAWyD6Gwn/Le3fVxxK6RPeeizDV6VfzHLxhxBNkMgmd
-QUkBkvqF154wYxhzsHn72ushbJpspKz1LQN7d5u6QOq3h2sLwcLbT457qbMfZsZs
-HLhoOibOd+yJ7C6TRbbyC4sQRr+K1CNGcvhJABEBAAG0NEpvaG4gRG9lIChUaGlz
-IGlzIGEgdGVzdCBrZXkpIDxqb2huLmRvZUBleGFtcGxlLm9yZz6JAU4EEwEIADgW
-IQSxqGaTVBU7eZ8iF78wchXBPfepZAUCXkUl/wIbAwULCQgHAgYVCgkICwIEFgID
-AQIeAQIXgAAKCRAwchXBPfepZF4iB/49B7q4AfO3xHEa8LK2H+f7Mnm4dRfS2YPo
-v2p6TRe1h2DxwpTevNQUhXw2U0nfRIEKBAZqgb7NVktkoh0DWtKatms2yHMAS+ah
-lQoHb2gRgXa9M9Tq0x5u9sl0NYnx7Wu5uu6Ybw9luPKoAfO91T0vei0p3eMn3fIV
-0O012ITvmgKJPppQDKFJHGZJMbVDO4TNxP89HgyhB41RO7AZadvu73S00x2K6x+O
-R4s/++4Y98vScCPm3DUOXeoHXKGqFcNYTxJL9bsE2I0uYgvJSxNoK1dVnmvxp3zz
-hcxAdzizgMz0ufY6YLMCjy5MDOzPARkmYPXdkJ6jceOIqGLUw1kquQENBF5FJf8B
-CACpsh5cyHB7eEwQvLzJVsXpTW0Rh/Fe36AwC2Vz13WeE6GFrOvw1qATvtYB1919
-M4B44YH9J7I5SrFZad86Aw4n5Gi0BwLlGNa/oCMvYzlNHaTXURA271ghJqdZizqV
-UETj3WNoaYm4mYMfb0dcayDJvVPWP7InzOsdIRU9WXBUSyVMxNMXccr2UvIuhdPg
-lmVT8NtsWR+q8xBoL2Dp0ojYLVD3MlwKe1pE5mEwasYCkWePLWyGdfDW1MhUDsPH
-3K1IjpPLWU9FBk8KM4z8WooY9/kyMIyRw39MvOHGfgcFBpiZwlELNZGSFhbRun03
-PMk2Qd3k+0FGV1IhFAYsr7QRABEBAAGJATYEGAEIACAWIQSxqGaTVBU7eZ8iF78w
-chXBPfepZAUCXkUl/wIbDAAKCRAwchXBPfepZOtqB/9xsGEgQgm70KYID39H91k4
-ef/RlpRDY1ndC0MoPfqE03IEXTC/MjtU+ksPKEoZeQsxVaUJ2WBueI5WGJ3Y73pO
-HAd7N0SyGHT5s6gK1FSx29be1qiPwUu5KR2jpm3RjgpbymnOWe4C6iiYCFQ85IX+
-LzpE+p9bB02PUrmzOb4MBV6E5mg30UjXIX01+bwZq5XSB4/FaUrQOAxLuRvVRjK0
-CEcFbPGIlkPSW6s4M9xCC2sQi7caFKVK6Zqf78KbOwAHqfS0x9u2jtTIhsgCjGTI
-AOQ5lNwpLEMjwLias6e5sM6hcK9Wo+A9Sw23f8lMau5clOZTJeyAUAff+5anTnUn
-=ZjQT
+mQENBGcUGPEBCACox9bw5BiN9M+1qVtU90bkHl5xzPDl8SqX/2ieYSx0ZfUpmRAH
+9EbW4j54cTFM6mX18Yv2LRWQhHjzslPietJ1Lb3PGY2ffDDxJsq/uQHK/ztqePc7
+omJJjUuF5D7BjuOq/MFyu7dWSCXOrj8soY9HIS96pPNTF9ykLDhqKWIqGA7pORKk
+RFczMLmEojLKefHvgtp9ikNNbIJyq/P5hNHr/DfC7rFaMTrXNc2xP2MD7MYNdVmT
+N2NN/X676rTsu8ltUi96F5PR33mGez6Z66yMjJf863bd+muq8552ExoQGQ/uGo5y
+wvwoEOF7hx1Z6JYl56hAICXPL/ZOZTPdBf+9ABEBAAG0NEphbmUgRG9lIChUaGlz
+IGlzIGEgdGVzdCBrZXkpIDxqYW5lLmRvZUBleGFtcGxlLm9yZz6JAVEEEwEIADsW
+IQSoQ3iEudN9vdxgn6xy8nGZUc/d5AUCZxQY8QIbAwULCQgHAgIiAgYVCgkICwIE
+FgIDAQIeBwIXgAAKCRBy8nGZUc/d5ConB/9Z39ufzGmplm0m9ylN+x8iNYJJ5rk6
+WhnwDsKSEDPoYnSUuESQ7zxhPkqr2amgAcFWba6vm+GvdFBB+y8JzSGIBmNmQfuw
+dtBd5EI+cTSTzuXo4NXR7TrMJGPP8IvJNSrliG61JnW3kcz9U9dywum+XF57+2X1
+KCt3npJI64sMX39QZ1ReaRbKWrKcBdCWZqW79KbFn4yl4ooMS9aKggQQP91feMA9
+dP3onL+TWLRKVMQ657OngTKi8rIez+RasRmVV3Av+GMl0Tdcg3sWHrlliBexmC/X
+mHzbl/PR8HAjWxie+pObGPz1aodJpeI0Lr5LQgJxZtx49kov9Ua5xVUxuQENBGcU
+GPEBCACmVEII6Igka7AVqCrUrdRonSzuelT6X6/VToBoJMER7q5MENtqWd0iby4N
+kIJxaJQFyXY7mYyZqf2aRbCu+cvh/F77iSZEOzNoJuut5sjPg7MM+s/9GRlYboq9
+RGqDJwoT7+k6cdUJON5UPvdJj8GnFGGu9ZFs/cOz2psggzfeV4YbTKXzFm2yKMpx
+LdeBeLXLYG46d0ChZMmKyBLLJWtUb71MU2TTWyrmtDoN02bxDQpAeJu+3Qp6lq+/
+CGe5f407jkx2PDKvV6HkuYzjs8apVFVZsBkDlhkaX5YdFI2r1TxIbxC9k2UG9VLJ
+lGNeqO3iUCsjuKd7iaiLGGBIeqKnABEBAAGJATYEGAEIACAWIQSoQ3iEudN9vdxg
+n6xy8nGZUc/d5AUCZxQY8QIbDAAKCRBy8nGZUc/d5OxbB/sEqrdtCMFrXLOU7dur
+or1lfrlYaOIaOup+/SnTSi688O0ixZ2XjV7CW3z1E8JjWAVsQPdfpC2QOZATWZ/q
+ZMuEMwNpzhCVZDwBJR7nw+Pv/xFv9DvLEiJYHCyBrQtQ6vopG0t2yxJ4R/R48fQC
+m2xT54mb4flIV/C8zRy3eK2wY/kR5FVxnLwwFlYayR7+wuLTiHqqxRyeZA3hQcF3
+YDOgvRu3YzmESPtIBI6iNphfSSAAtkUqNJnwPAIxyky8xEInUZ7maOADRWgEH8uG
++1FjPta6cgZ1tJzFtJ7Bwa2///UAp7BQqDl7DyMQAfOZGkUI9mqEXdra4YqMv5X0
+Y2UQ
+=QL1U
-----END PGP PUBLIC KEY BLOCK-----
`
+
+const testOwnertrust = "B1A8669354153B799F2217BF307215C13DF7A964:6:\n"
diff --git a/lib/crypto/gpg/gpgbin/encrypt.go b/lib/crypto/gpg/gpgbin/encrypt.go
index fa33e466..712bb327 100644
--- a/lib/crypto/gpg/gpgbin/encrypt.go
+++ b/lib/crypto/gpg/gpgbin/encrypt.go
@@ -8,13 +8,10 @@ import (
"git.sr.ht/~rjarry/aerc/models"
)
-// Encrypt runs gpg --encrypt [--sign] -r [recipient]. The default is to have
-// --trust-model always set
+// Encrypt runs gpg --encrypt [--sign] -r [recipient]
func Encrypt(r io.Reader, to []string, from string) ([]byte, error) {
- // TODO probably shouldn't have --trust-model always a default
args := []string{
"--armor",
- "--trust-model", "always",
}
if from != "" {
args = append(args, "--sign", "--default-key", from)
diff --git a/lib/crypto/gpg/gpgbin/gpgbin.go b/lib/crypto/gpg/gpgbin/gpgbin.go
index a63dd886..a7eafacd 100644
--- a/lib/crypto/gpg/gpgbin/gpgbin.go
+++ b/lib/crypto/gpg/gpgbin/gpgbin.go
@@ -206,6 +206,11 @@ func parse(r io.Reader, md *models.MessageDetails) error {
md.SignatureError = "gpg: unsupported algorithm"
}
md.SignedBy = "(unknown signer)"
+ case "INV_RECP":
+ t := strings.SplitN(line, " ", 4)
+ if t[2] == "10" {
+ return fmt.Errorf("gpg: public key of %s is not trusted", t[3])
+ }
case "BEGIN_ENCRYPTION":
msgCollecting = true
case "SIG_CREATED":
diff --git a/lib/crypto/gpg/gpgbin/import-ownertrust.go b/lib/crypto/gpg/gpgbin/import-ownertrust.go
new file mode 100644
index 00000000..05499917
--- /dev/null
+++ b/lib/crypto/gpg/gpgbin/import-ownertrust.go
@@ -0,0 +1,16 @@
+package gpgbin
+
+import (
+ "io"
+)
+
+// Import runs gpg --import-ownertrust and thus imports trusts for keys
+func ImportOwnertrust(r io.Reader) error {
+ args := []string{"--import-ownertrust"}
+ g := newGpg(r, args)
+ err := g.cmd.Run()
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/lib/crypto/gpg/reader_test.go b/lib/crypto/gpg/reader_test.go
index 957a727d..fdb5c452 100644
--- a/lib/crypto/gpg/reader_test.go
+++ b/lib/crypto/gpg/reader_test.go
@@ -18,6 +18,11 @@ func importPublicKey() {
gpgbin.Import(r)
}
+func importOwnertrust() {
+ r := strings.NewReader(testOwnertrust)
+ gpgbin.ImportOwnertrust(r)
+}
+
type readerTestCase struct {
name string
want models.MessageDetails
@@ -26,8 +31,8 @@ type readerTestCase struct {
func TestReader(t *testing.T) {
initGPGtest(t)
- importPublicKey()
importSecretKey()
+ importOwnertrust()
testCases := []readerTestCase{
{
diff --git a/lib/crypto/gpg/writer_test.go b/lib/crypto/gpg/writer_test.go
index 0355db5e..26f3def7 100644
--- a/lib/crypto/gpg/writer_test.go
+++ b/lib/crypto/gpg/writer_test.go
@@ -16,26 +16,38 @@ func init() {
}
type writerTestCase struct {
- name string
- method string
- body string
+ name string
+ method string
+ body string
+ to []string
+ expectedErr string
}
func TestWriter(t *testing.T) {
initGPGtest(t)
- importPublicKey()
importSecretKey()
+ importPublicKey()
+ importOwnertrust()
testCases := []writerTestCase{
{
name: "Encrypt",
method: "encrypt",
body: "This is an encrypted message!\r\n",
+ to: []string{"john.doe@example.org"},
},
{
name: "Sign",
method: "sign",
body: "This is a signed message!\r\n",
+ to: []string{"john.doe@example.org"},
+ },
+ {
+ name: "Encrypt to untrusted",
+ method: "encrypt",
+ body: "This is an encrypted message!\r\n",
+ to: []string{"jane.doe@example.org"},
+ expectedErr: "gpg: failure to encrypt: gpg: public key of jane.doe@example.org is not trusted. check public key(s)",
},
}
var h textproto.Header
@@ -45,18 +57,18 @@ func TestWriter(t *testing.T) {
var header textproto.Header
header.Set("Content-Type", "text/plain")
- to := []string{"john.doe@example.org"}
from := "john.doe@example.org"
var err error
for _, tc := range testCases {
+ t.Logf("Test case: %s", tc.name)
var (
buf bytes.Buffer
cleartext io.WriteCloser
)
switch tc.method {
case "encrypt":
- cleartext, err = Encrypt(&buf, h, to, from)
+ cleartext, err = Encrypt(&buf, h, tc.to, from)
if err != nil {
t.Fatalf("Encrypt() = %v", err)
}
@@ -73,8 +85,14 @@ func TestWriter(t *testing.T) {
t.Fatalf("io.WriteString() = %v", err)
}
if err = cleartext.Close(); err != nil {
+ if err.Error() == tc.expectedErr {
+ continue
+ }
t.Fatalf("ciphertext.Close() = %v", err)
}
+ if tc.expectedErr != "" {
+ t.Fatalf("Expected error %v, but got %v", tc.expectedErr, err)
+ }
switch tc.method {
case "encrypt":
validateEncrypt(t, buf)