Compare commits

..

2 commits

Author SHA1 Message Date
Michael Fuchs
3e25812ef3
Merge faffc025cb into c47f08dc7f 2024-10-27 08:42:20 +00:00
theexiile1305
faffc025cb
feat: s/mime singing supports quoted printable encoding, also added tests 2024-10-27 09:42:13 +01:00
3 changed files with 131 additions and 13 deletions

7
msg.go
View file

@ -2184,8 +2184,11 @@ func (m *Msg) signMessage(msg *Msg) (*Msg, error) {
// createSignaturePart creates an additional part that be used for storing the S/MIME signature of the given body
func (m *Msg) createSignaturePart(encoding Encoding, contentType ContentType, charSet Charset, body []byte) (*Part, error) {
message := m.sMime.prepareMessage(encoding, contentType, charSet, body)
signedMessage, err := m.sMime.signMessage(message)
message, err := m.sMime.prepareMessage(encoding, contentType, charSet, body)
if err != nil {
return nil, err
}
signedMessage, err := m.sMime.signMessage(*message)
if err != nil {
return nil, err
}

View file

@ -13,6 +13,7 @@ import (
"encoding/pem"
"errors"
"fmt"
"mime/quotedprintable"
"strings"
)
@ -119,8 +120,32 @@ func (sm *SMime) signMessage(message string) (*string, error) {
}
// createMessage prepares the message that will be used for the sign method later
func (sm *SMime) prepareMessage(encoding Encoding, contentType ContentType, charset Charset, body []byte) string {
return fmt.Sprintf("Content-Transfer-Encoding: %v\r\nContent-Type: %v; charset=%v\r\n\r\n%v", encoding, contentType, charset, string(body))
func (sm *SMime) prepareMessage(encoding Encoding, contentType ContentType, charset Charset, body []byte) (*string, error) {
encodedMessage, err := sm.encodeMessage(encoding, string(body))
if err != nil {
return nil, err
}
preparedMessage := fmt.Sprintf("Content-Transfer-Encoding: %v\r\nContent-Type: %v; charset=%v\r\n\r\n%v", encoding, contentType, charset, *encodedMessage)
return &preparedMessage, nil
}
// encodeMessage encodes the message with the given encoding
func (sm *SMime) encodeMessage(encoding Encoding, message string) (*string, error) {
if encoding != EncodingQP {
return &message, nil
}
buffer := bytes.Buffer{}
writer := quotedprintable.NewWriter(&buffer)
if _, err := writer.Write([]byte(message)); err != nil {
return nil, err
}
if err := writer.Close(); err != nil {
return nil, err
}
encodedMessage := buffer.String()
return &encodedMessage, nil
}
// encodeToPEM uses the method pem.Encode from the standard library but cuts the typical PEM preamble

View file

@ -121,19 +121,109 @@ func TestPrepareMessage(t *testing.T) {
contentType := TypeTextPlain
charset := CharsetUTF8
body := []byte("This is the body!")
result := sMime.prepareMessage(encoding, contentType, charset, body)
result, err := sMime.prepareMessage(encoding, contentType, charset, body)
if err != nil {
t.Errorf("Error preparing message: %s", err)
}
if !strings.Contains(result, encoding.String()) {
t.Errorf("createMessage() did not return the correct encoding")
if !strings.Contains(*result, encoding.String()) {
t.Errorf("prepareMessage() did not return the correct encoding")
}
if !strings.Contains(result, contentType.String()) {
t.Errorf("createMessage() did not return the correct contentType")
if !strings.Contains(*result, contentType.String()) {
t.Errorf("prepareMessage() did not return the correct contentType")
}
if !strings.Contains(result, string(body)) {
t.Errorf("createMessage() did not return the correct body")
if !strings.Contains(*result, string(body)) {
t.Errorf("prepareMessage() did not return the correct body")
}
if result != fmt.Sprintf("Content-Transfer-Encoding: %s\r\nContent-Type: %s; charset=%s\r\n\r\n%s", encoding, contentType, charset, string(body)) {
t.Errorf("createMessage() did not sucessfully create the message")
if *result != fmt.Sprintf("Content-Transfer-Encoding: %s\r\nContent-Type: %s; charset=%s\r\n\r\n%s", encoding, contentType, charset, string(body)) {
t.Errorf("prepareMessage() did not sucessfully create the message")
}
}
// TestPrepareMessage_QuotedPrintable tests the prepareMessage method with quoted printable encoding
func TestPrepareMessage_QuotedPrintable(t *testing.T) {
privateKey, certificate, intermediateCertificate, err := getDummyRSACryptoMaterial()
if err != nil {
t.Errorf("Error getting dummy crypto material: %s", err)
}
sMime, err := newSMimeWithRSA(privateKey, certificate, intermediateCertificate)
if err != nil {
t.Errorf("Error creating new SMime from keyPair: %s", err)
}
body := "This is the body with special chars like äöü ÄÖÜ ß!"
quotedPrintableBody := "This is the body with special chars like =C3=A4=C3=B6=C3=BC =C3=84=C3=96=C3=\r\n=9C =C3=9F!"
encoding := EncodingQP
contentType := TypeTextPlain
charset := CharsetUTF8
result, err := sMime.prepareMessage(encoding, contentType, charset, []byte(body))
if err != nil {
t.Errorf("Error preparing message: %s", err)
}
if !strings.Contains(*result, encoding.String()) {
t.Errorf("prepareMessage() did not return the correct encoding")
}
if !strings.Contains(*result, contentType.String()) {
t.Errorf("prepareMessage() did not return the correct contentType")
}
if !strings.Contains(*result, quotedPrintableBody) {
t.Errorf("prepareMessage() did not return the correct body")
}
if *result != fmt.Sprintf("Content-Transfer-Encoding: %s\r\nContent-Type: %s; charset=%s\r\n\r\n%s", encoding, contentType, charset, quotedPrintableBody) {
t.Errorf("prepareMessage() did not sucessfully create the message")
}
}
// TestEncodeMessage tests the TestEncodeMessage method without any encoding
func TestEncodeMessage(t *testing.T) {
body := "This is the body with special chars like äöü ÄÖÜ ß!"
encoding := EncodingUSASCII
privateKey, certificate, intermediateCertificate, err := getDummyRSACryptoMaterial()
if err != nil {
t.Errorf("Error getting dummy crypto material: %s", err)
}
sMime, err := newSMimeWithRSA(privateKey, certificate, intermediateCertificate)
if err != nil {
t.Errorf("Error creating new SMime from keyPair: %s", err)
}
result, err := sMime.encodeMessage(encoding, body)
if err != nil {
t.Errorf("Error preparing message: %s", err)
}
if *result != body {
t.Errorf("encodeMessage() did not return the correct encoded message: %s", *result)
}
}
// TestEncodeMessage_QuotedPrintable tests the TestEncodeMessage method with quoted printable body
func TestEncodeMessage_QuotedPrintable(t *testing.T) {
body := "This is the body with special chars like äöü ÄÖÜ ß!"
quotedPrintableBody := "This is the body with special chars like =C3=A4=C3=B6=C3=BC =C3=84=C3=96=C3=\r\n=9C =C3=9F!"
encoding := EncodingQP
privateKey, certificate, intermediateCertificate, err := getDummyRSACryptoMaterial()
if err != nil {
t.Errorf("Error getting dummy crypto material: %s", err)
}
sMime, err := newSMimeWithRSA(privateKey, certificate, intermediateCertificate)
if err != nil {
t.Errorf("Error creating new SMime from keyPair: %s", err)
}
result, err := sMime.encodeMessage(encoding, body)
if err != nil {
t.Errorf("Error preparing message: %s", err)
}
if *result != quotedPrintableBody {
t.Errorf("encodeMessage() did not return the correct encoded message: %s", *result)
}
}