diff --git a/openpgp/packet/config.go b/openpgp/packet/config.go index 6e8d25773..f9208158b 100644 --- a/openpgp/packet/config.go +++ b/openpgp/packet/config.go @@ -85,6 +85,15 @@ type Config struct { // when producing a generic certification signature onto an existing user ID. // The identity must be present in the signer Entity. SigningIdentity string + // InsecureAllowUnauthenticatedMessages controls, whether it is tolerated to read + // encrypted messages without Modification Detection Code (MDC). + // MDC is mandated by the IETF OpenPGP Crypto Refresh draft and has long been implemented + // in most OpenPGP implementations. Messages without MDC are considered unnecessarily + // insecure and should be prevented whenever possible. + // In case one needs to deal with messages from very old OpenPGP implementations, there + // might be no other way than to tolerate the missing MDC. Setting this flag, allows this + // mode of operation. It should be considered a measure of last resort. + InsecureAllowUnauthenticatedMessages bool } func (c *Config) Random() io.Reader { @@ -186,3 +195,10 @@ func (c *Config) SigningUserId() string { } return c.SigningIdentity } + +func (c *Config) AllowUnauthenticatedMessages() bool { + if c == nil { + return false + } + return c.InsecureAllowUnauthenticatedMessages +} diff --git a/openpgp/packet/symmetrically_encrypted.go b/openpgp/packet/symmetrically_encrypted.go index ad864e945..8b84de177 100644 --- a/openpgp/packet/symmetrically_encrypted.go +++ b/openpgp/packet/symmetrically_encrypted.go @@ -37,8 +37,6 @@ func (se *SymmetricallyEncrypted) parse(r io.Reader) error { if buf[0] != symmetricallyEncryptedVersion { return errors.UnsupportedError("unknown SymmetricallyEncrypted version") } - } else { - return errors.UnsupportedError("Symmetrically encrypted packets without MDC are not supported") } se.Contents = r return nil diff --git a/openpgp/read.go b/openpgp/read.go index d8985c8e6..f9233f6d2 100644 --- a/openpgp/read.go +++ b/openpgp/read.go @@ -130,8 +130,14 @@ ParsePackets: pubKeys = append(pubKeys, keyEnvelopePair{k, p}) } } - case *packet.SymmetricallyEncrypted, *packet.AEADEncrypted: - edp = p.(packet.EncryptedDataPacket) + case *packet.SymmetricallyEncrypted: + if !p.MDC && !config.AllowUnauthenticatedMessages() { + return nil, errors.UnsupportedError("message is not authenticated") + } + edp = p + break ParsePackets + case *packet.AEADEncrypted: + edp = p break ParsePackets case *packet.Compressed, *packet.LiteralData, *packet.OnePassSignature: // This message isn't encrypted. diff --git a/openpgp/read_test.go b/openpgp/read_test.go index 6fb35f2ca..664a939d8 100644 --- a/openpgp/read_test.go +++ b/openpgp/read_test.go @@ -716,3 +716,56 @@ func TestCorruptedMessageWrongLength(t *testing.T) { t.Errorf("Expected error '%s', but got error '%s'", expectedErr, err) } } + +func TestMessageWithoutMdc(t *testing.T) { + armored, err := os.Open("test_data/aead-ocb-asym-key.asc") + if err != nil { + t.Fatal(err) + } + defer armored.Close() + + el, err := ReadArmoredKeyRing(armored) + if err != nil { + t.Fatal(err) + } + + armoredMessageWithoutMdc, err := ioutil.ReadFile("test_data/sym-message-without-mdc.asc") + if err != nil { + t.Fatal(err) + } + + t.Run("fails with InsecureAllowUnauthenticatedMessages disabled", func(t *testing.T) { + messageWithoutMdc, err := armor.Decode(bytes.NewReader(armoredMessageWithoutMdc)) + if err != nil { + t.Fatal(err) + } + + _, err = ReadMessage(messageWithoutMdc.Body, el, nil, nil) + if err == nil { + t.Fatal("reading the message should have failed") + } + }) + + t.Run("succeeds with InsecureAllowUnauthenticatedMessages enabled", func(t *testing.T) { + messageWithoutMdc, err := armor.Decode(bytes.NewReader(armoredMessageWithoutMdc)) + if err != nil { + t.Fatal(err) + } + + md, err := ReadMessage(messageWithoutMdc.Body, el, nil, &packet.Config{ + InsecureAllowUnauthenticatedMessages: true, + }) + if err != nil { + t.Fatal("reading the message should have worked") + } + + b, err := ioutil.ReadAll(md.UnverifiedBody) + if err != nil { + t.Fatal("reading the message should have worked") + } + + if !bytes.Equal(b, []byte("message without mdc\n")) { + t.Error("unexpected message content") + } + }) +} diff --git a/openpgp/test_data/sym-message-without-mdc.asc b/openpgp/test_data/sym-message-without-mdc.asc new file mode 100644 index 000000000..e28fd5ce9 --- /dev/null +++ b/openpgp/test_data/sym-message-without-mdc.asc @@ -0,0 +1,8 @@ +-----BEGIN PGP MESSAGE----- + +hF4DaLVM+JXUh9ESAQdA7E42KKhtMn7vjYN+CIcoM4QMXODako4DQPYLwhYjHTow +hBVmQWk+WyyDjqB2yZEJPVTDYSvebBynwCGN9AFR6u5dXjFqtNNX6EJEJBzLLL2+ +yUMNiMt8/ujn4y4GNaZEbFdf4+K/oQbz6fvQgNipWZGg2Ys0foHi50EzhTDyVG7s +nwkVrIpSuhaZLoqzxbizw5o6YVMc +=//7h +-----END PGP MESSAGE-----