feat: s/mime singing supports quoted printable encoding, also added tests

This commit is contained in:
theexiile1305 2024-10-27 09:42:13 +01:00
parent cc4c5bfd04
commit faffc025cb
No known key found for this signature in database
GPG key ID: A1BDDE98F2BF6E40
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 // 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) { func (m *Msg) createSignaturePart(encoding Encoding, contentType ContentType, charSet Charset, body []byte) (*Part, error) {
message := m.sMime.prepareMessage(encoding, contentType, charSet, body) message, err := m.sMime.prepareMessage(encoding, contentType, charSet, body)
signedMessage, err := m.sMime.signMessage(message) if err != nil {
return nil, err
}
signedMessage, err := m.sMime.signMessage(*message)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -13,6 +13,7 @@ import (
"encoding/pem" "encoding/pem"
"errors" "errors"
"fmt" "fmt"
"mime/quotedprintable"
"strings" "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 // 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 { func (sm *SMime) prepareMessage(encoding Encoding, contentType ContentType, charset Charset, body []byte) (*string, error) {
return fmt.Sprintf("Content-Transfer-Encoding: %v\r\nContent-Type: %v; charset=%v\r\n\r\n%v", encoding, contentType, charset, string(body)) 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 // 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 contentType := TypeTextPlain
charset := CharsetUTF8 charset := CharsetUTF8
body := []byte("This is the body!") 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()) { if !strings.Contains(*result, encoding.String()) {
t.Errorf("createMessage() did not return the correct encoding") t.Errorf("prepareMessage() did not return the correct encoding")
} }
if !strings.Contains(result, contentType.String()) { if !strings.Contains(*result, contentType.String()) {
t.Errorf("createMessage() did not return the correct contentType") t.Errorf("prepareMessage() did not return the correct contentType")
} }
if !strings.Contains(result, string(body)) { if !strings.Contains(*result, string(body)) {
t.Errorf("createMessage() did not return the correct 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)) { 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") 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)
} }
} }