Canonicalize line endings for text messages (#86)

* Canonicalize line endings for text messages

* Improve cleartext messages
This commit is contained in:
wussler 2020-10-12 21:24:33 +02:00 committed by GitHub
parent a4d89bce32
commit ce607e0fa8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 25 additions and 25 deletions

View file

@ -9,6 +9,7 @@ import (
"io/ioutil" "io/ioutil"
"regexp" "regexp"
"runtime" "runtime"
"strings"
"github.com/ProtonMail/gopenpgp/v2/armor" "github.com/ProtonMail/gopenpgp/v2/armor"
"github.com/ProtonMail/gopenpgp/v2/constants" "github.com/ProtonMail/gopenpgp/v2/constants"
@ -86,7 +87,7 @@ func NewPlainMessageFromFile(data []byte, filename string, time uint32) *PlainMe
// ready for encryption, signature, or verification from an unencrypted string. // ready for encryption, signature, or verification from an unencrypted string.
func NewPlainMessageFromString(text string) *PlainMessage { func NewPlainMessageFromString(text string) *PlainMessage {
return &PlainMessage{ return &PlainMessage{
Data: []byte(text), Data: []byte(strings.ReplaceAll(strings.ReplaceAll(text, "\r\n", "\n"), "\n", "\r\n")),
TextType: true, TextType: true,
time: uint32(GetUnixTime()), time: uint32(GetUnixTime()),
} }
@ -195,7 +196,7 @@ func (msg *PlainMessage) GetBinary() []byte {
// GetString returns the content of the message as a string. // GetString returns the content of the message as a string.
func (msg *PlainMessage) GetString() string { func (msg *PlainMessage) GetString() string {
return string(msg.Data) return strings.ReplaceAll(string(msg.Data), "\r\n", "\n")
} }
// GetBase64 returns the base-64 encoded binary content of the message as a // GetBase64 returns the base-64 encoded binary content of the message as a

View file

@ -33,7 +33,7 @@ func (keyRing *KeyRing) DecryptMIMEMessage(
return return
} }
body, attachments, attachmentHeaders, err := parseMIME(decryptedMessage.GetString(), verifyKey) body, attachments, attachmentHeaders, err := parseMIME(string(decryptedMessage.GetBinary()), verifyKey)
if err != nil { if err != nil {
callbacks.OnError(err) callbacks.OnError(err)
return return

View file

@ -51,7 +51,8 @@ func TestVerifyTextDetachedSigWrong(t *testing.T) {
func TestSignBinDetached(t *testing.T) { func TestSignBinDetached(t *testing.T) {
var err error var err error
binSignature, err = keyRingTestPrivate.SignDetached(NewPlainMessage([]byte(signedPlainText))) message = NewPlainMessage([]byte(signedPlainText))
binSignature, err = keyRingTestPrivate.SignDetached(message)
if err != nil { if err != nil {
t.Fatal("Cannot generate signature:", err) t.Fatal("Cannot generate signature:", err)
} }

View file

@ -1,8 +1,6 @@
package helper package helper
import ( import (
"strings"
"github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/ProtonMail/gopenpgp/v2/internal" "github.com/ProtonMail/gopenpgp/v2/internal"
) )
@ -50,7 +48,7 @@ func VerifyCleartextMessageArmored(publicKey, armored string, verifyTime int64)
// SignCleartextMessage signs text given a private keyring, canonicalizes and // SignCleartextMessage signs text given a private keyring, canonicalizes and
// trims the newlines, and returns the PGP-compliant special armoring. // trims the newlines, and returns the PGP-compliant special armoring.
func SignCleartextMessage(keyRing *crypto.KeyRing, text string) (string, error) { func SignCleartextMessage(keyRing *crypto.KeyRing, text string) (string, error) {
text = canonicalizeAndTrim(text) text = internal.TrimWhitespace(text)
message := crypto.NewPlainMessageFromString(text) message := crypto.NewPlainMessageFromString(text)
signature, err := keyRing.SignDetached(message) signature, err := keyRing.SignDetached(message)
@ -79,12 +77,3 @@ func VerifyCleartextMessage(keyRing *crypto.KeyRing, armored string, verifyTime
return message.GetString(), nil return message.GetString(), nil
} }
// ----- INTERNAL FUNCTIONS -----
// canonicalizeAndTrim alters a string canonicalizing and trimming the newlines.
func canonicalizeAndTrim(text string) string {
text = internal.TrimNewlines(text)
text = strings.ReplaceAll(strings.ReplaceAll(text, "\r\n", "\n"), "\n", "\r\n")
return text
}

View file

@ -2,13 +2,16 @@ package helper
import ( import (
"regexp" "regexp"
"strings"
"testing" "testing"
"github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/ProtonMail/gopenpgp/v2/internal"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
const signedPlainText = "Signed message\n" const inputPlainText = " Signed message\n \n "
const signedPlainText = " Signed message\n\n"
var signedMessageTest = regexp.MustCompile( var signedMessageTest = regexp.MustCompile(
"(?s)^-----BEGIN PGP SIGNED MESSAGE-----.*-----BEGIN PGP SIGNATURE-----.*-----END PGP SIGNATURE-----$") "(?s)^-----BEGIN PGP SIGNED MESSAGE-----.*-----BEGIN PGP SIGNATURE-----.*-----END PGP SIGNATURE-----$")
@ -18,7 +21,7 @@ func TestSignClearText(t *testing.T) {
armored, err := SignCleartextMessageArmored( armored, err := SignCleartextMessageArmored(
readTestFile("keyring_privateKey", false), readTestFile("keyring_privateKey", false),
testMailboxPassword, testMailboxPassword,
signedPlainText, inputPlainText,
) )
if err != nil { if err != nil {
@ -36,11 +39,17 @@ func TestSignClearText(t *testing.T) {
t.Fatal("Cannot verify message:", err) t.Fatal("Cannot verify message:", err)
} }
assert.Exactly(t, canonicalizeAndTrim(signedPlainText), verified) assert.Exactly(t, signedPlainText, verified)
clearTextMessage, err := crypto.NewClearTextMessageFromArmored(armored)
if err != nil {
t.Fatal("Cannot parse message:", err)
}
assert.Exactly(t, canonicalizeAndTrim(inputPlainText), string(clearTextMessage.GetBinary()))
} }
func TestMessageCanonicalizeAndTrim(t *testing.T) { func canonicalizeAndTrim(text string) string {
text := "Hi \ntest!\r\n\n" text = internal.TrimWhitespace(text)
canon := canonicalizeAndTrim(text) text = strings.ReplaceAll(strings.ReplaceAll(text, "\r\n", "\n"), "\n", "\r\n")
assert.Exactly(t, "Hi\r\ntest!\r\n\r\n", canon) return text
} }

View file

@ -7,9 +7,9 @@ import (
"github.com/ProtonMail/gopenpgp/v2/constants" "github.com/ProtonMail/gopenpgp/v2/constants"
) )
// TrimNewlines removes whitespace from the end of each line of the input // TrimWhitespace removes whitespace from the end of each line of the input
// string. // string.
func TrimNewlines(input string) string { func TrimWhitespace(input string) string {
var re = regexp.MustCompile(`(?m)[ \t]*$`) var re = regexp.MustCompile(`(?m)[ \t]*$`)
return re.ReplaceAllString(input, "") return re.ReplaceAllString(input, "")
} }