In swift, strings must be strictly utf8, and when golang returns a string with non utf8 characters, it gets translated to an empty string for utf8. To avoid this situation, we sanitize strings before returning them. This behavior is only enabled when building with the "ios" build tag.
118 lines
3.6 KiB
Go
118 lines
3.6 KiB
Go
package crypto
|
|
|
|
import (
|
|
"bytes"
|
|
"io/ioutil"
|
|
"net/mail"
|
|
"net/textproto"
|
|
"strings"
|
|
|
|
"github.com/ProtonMail/go-crypto/openpgp"
|
|
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
|
gomime "github.com/ProtonMail/go-mime"
|
|
"github.com/ProtonMail/gopenpgp/v2/constants"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// MIMECallbacks defines callback methods to process a MIME message.
|
|
type MIMECallbacks interface {
|
|
OnBody(body string, mimetype string)
|
|
OnAttachment(headers string, data []byte)
|
|
// Encrypted headers can be in an attachment and thus be placed at the end of the mime structure.
|
|
OnEncryptedHeaders(headers string)
|
|
OnVerified(verified int)
|
|
OnError(err error)
|
|
}
|
|
|
|
// DecryptMIMEMessage decrypts a MIME message.
|
|
func (keyRing *KeyRing) DecryptMIMEMessage(
|
|
message *PGPMessage, verifyKey *KeyRing, callbacks MIMECallbacks, verifyTime int64,
|
|
) {
|
|
decryptedMessage, err := keyRing.Decrypt(message, verifyKey, verifyTime)
|
|
embeddedSigError, err := separateSigError(err)
|
|
if err != nil {
|
|
callbacks.OnError(err)
|
|
return
|
|
}
|
|
body, attachments, attachmentHeaders, err := parseMIME(string(decryptedMessage.GetBinary()), verifyKey)
|
|
mimeSigError, err := separateSigError(err)
|
|
if err != nil {
|
|
callbacks.OnError(err)
|
|
return
|
|
}
|
|
// We only consider the signature to be failed if both embedded and mime verification failed
|
|
if embeddedSigError != nil && mimeSigError != nil {
|
|
callbacks.OnError(embeddedSigError)
|
|
callbacks.OnError(mimeSigError)
|
|
callbacks.OnVerified(prioritizeSignatureErrors(embeddedSigError, mimeSigError))
|
|
} else if verifyKey != nil {
|
|
callbacks.OnVerified(constants.SIGNATURE_OK)
|
|
}
|
|
bodyContent, bodyMimeType := body.GetBody()
|
|
bodyContentSanitized := sanitizeString(bodyContent)
|
|
callbacks.OnBody(bodyContentSanitized, bodyMimeType)
|
|
for i := 0; i < len(attachments); i++ {
|
|
callbacks.OnAttachment(attachmentHeaders[i], []byte(attachments[i]))
|
|
}
|
|
callbacks.OnEncryptedHeaders("")
|
|
}
|
|
|
|
// ----- INTERNAL FUNCTIONS -----
|
|
|
|
func prioritizeSignatureErrors(signatureErrs ...*SignatureVerificationError) (maxError int) {
|
|
// select error with the highest value, if any
|
|
// FAILED > NO VERIFIER > NOT SIGNED > SIGNATURE OK
|
|
maxError = constants.SIGNATURE_OK
|
|
for _, err := range signatureErrs {
|
|
if err.Status > maxError {
|
|
maxError = err.Status
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func separateSigError(err error) (*SignatureVerificationError, error) {
|
|
sigErr := &SignatureVerificationError{}
|
|
if errors.As(err, sigErr) {
|
|
return sigErr, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
func parseMIME(
|
|
mimeBody string, verifierKey *KeyRing,
|
|
) (*gomime.BodyCollector, []string, []string, error) {
|
|
mm, err := mail.ReadMessage(strings.NewReader(mimeBody))
|
|
if err != nil {
|
|
return nil, nil, nil, errors.Wrap(err, "gopenpgp: error in reading message")
|
|
}
|
|
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}
|
|
|
|
h := textproto.MIMEHeader(mm.Header)
|
|
mmBodyData, err := ioutil.ReadAll(mm.Body)
|
|
if err != nil {
|
|
return nil, nil, nil, errors.Wrap(err, "gopenpgp: error in reading message body data")
|
|
}
|
|
|
|
printAccepter := gomime.NewMIMEPrinter()
|
|
bodyCollector := gomime.NewBodyCollector(printAccepter)
|
|
attachmentsCollector := gomime.NewAttachmentsCollector(bodyCollector)
|
|
mimeVisitor := gomime.NewMimeVisitor(attachmentsCollector)
|
|
|
|
var verifierEntities openpgp.KeyRing
|
|
if verifierKey != nil {
|
|
verifierEntities = verifierKey.entities
|
|
}
|
|
|
|
signatureCollector := newSignatureCollector(mimeVisitor, verifierEntities, config)
|
|
|
|
err = gomime.VisitAll(bytes.NewReader(mmBodyData), h, signatureCollector)
|
|
if err == nil && verifierKey != nil {
|
|
err = signatureCollector.verified
|
|
}
|
|
|
|
return bodyCollector,
|
|
attachmentsCollector.GetAttachments(),
|
|
attachmentsCollector.GetAttHeaders(),
|
|
err
|
|
}
|