From 34babb3b90af1335122046c40d77656d450e07bf Mon Sep 17 00:00:00 2001 From: Kay Lukas Date: Tue, 4 Sep 2018 23:40:35 +0200 Subject: [PATCH] WIP extract mime body and check signature --- mime.go | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 151 insertions(+), 7 deletions(-) diff --git a/mime.go b/mime.go index 2b61b5e..a75c30a 100644 --- a/mime.go +++ b/mime.go @@ -11,6 +11,10 @@ import ( log "github.com/Sirupsen/logrus" "mime" "mime/multipart" + "bufio" + "fmt" + "golang.org/x/crypto/openpgp/packet" + "golang.org/x/crypto/openpgp" ) @@ -29,16 +33,51 @@ func DecodePart(partReader io.Reader, header textproto.MIMEHeader) (decodedPart // them as a string type SignatureCollector struct { - target pmmime.VisitAcceptor + config packet.Config + keyring openpgp.KeyRing + target pmmime.VisitAcceptor signature string + verified int } -func NewSignatureCollector(targetAccepter pmmime.VisitAcceptor) *SignatureCollector { +func NewSignatureCollector(config packet.Config, targetAccepter pmmime.VisitAcceptor) *SignatureCollector { return &SignatureCollector{ target: targetAccepter, + config: config, + } } +func getRawMimePart(rawdata io.Reader, boundary string) (io.Reader, io.Reader) { + b, _ := ioutil.ReadAll(rawdata) + tee := bytes.NewReader(b) + + reader := bufio.NewReader(bytes.NewReader(b)) + byteBoundary := []byte(boundary) + bodyBuffer := &bytes.Buffer{} + for { + line, _, err := reader.ReadLine() + if err != nil { + return tee, bytes.NewReader(bodyBuffer.Bytes()) + } + if bytes.HasPrefix(line, byteBoundary) { + break + } + } + for { + line, _, err := reader.ReadLine() + if err != nil { + return tee, bytes.NewReader(bodyBuffer.Bytes()) + } + if bytes.HasPrefix(line, byteBoundary) { + break + } + bodyBuffer.Write(line) + } + ioutil.ReadAll(reader) + return tee, bytes.NewReader(bodyBuffer.Bytes()) +} + func getMultipartParts(r io.Reader, params map[string]string) (parts []io.Reader, headers []textproto.MIMEHeader, err error) { mr := multipart.NewReader(r, params["boundary"]) parts = []io.Reader{} @@ -62,12 +101,23 @@ func getMultipartParts(r io.Reader, params map[string]string) (parts []io.Reader return } +func verifyMime(body io.Reader, bodyHeader textproto.MIMEHeader, signature io.Reader) (err error) { + rawData, err := ioutil.ReadAll(body) + if err != nil { + return err + } + + decodedBodyStream := pmmime.DecodeContentEncoding(bytes.NewReader(rawData), bodyHeader.Get("Content-Transfer-Encoding")) + +} + func (sc *SignatureCollector) Accept(part io.Reader, header textproto.MIMEHeader, hasPlainSibling bool, isFirst, isLast bool) (err error) { parentMediaType, params, _ := mime.ParseMediaType(header.Get("Content-Type")) if parentMediaType == "multipart/signed" { + newPart, rawBody := getRawMimePart(part, params["boundary"]) var multiparts []io.Reader var multipartHeaders []textproto.MIMEHeader - if multiparts, multipartHeaders, err = getMultipartParts(part, params); err != nil { + if multiparts, multipartHeaders, err = getMultipartParts(newPart, params); err != nil { return } else { hasPlainChild := false @@ -78,7 +128,9 @@ func (sc *SignatureCollector) Accept(part io.Reader, header textproto.MIMEHeader } } if len(multiparts) != 2 { + sc.verified = notSigned // Invalid multipart/signed format just pass along + ioutil.ReadAll(rawBody) for i, p := range multiparts { if err = sc.target.Accept(p, multipartHeaders[i], hasPlainChild, true, true); err != nil { return @@ -86,13 +138,14 @@ func (sc *SignatureCollector) Accept(part io.Reader, header textproto.MIMEHeader } return } + // actual multipart/signed format err = sc.target.Accept(multiparts[0], multipartHeaders[0], hasPlainChild, true, true) if err != nil { return } partData, _ := ioutil.ReadAll(multiparts[1]) - decodedPart := pmmime.DecodeContentEncoding(bytes.NewReader(partData), header.Get("Content-Transfer-Encoding")) + decodedPart := pmmime.DecodeContentEncoding(bytes.NewReader(partData), multipartHeaders[1].Get("Content-Transfer-Encoding")) buffer, err := ioutil.ReadAll(decodedPart) if err != nil { return err @@ -102,7 +155,15 @@ func (sc *SignatureCollector) Accept(part io.Reader, header textproto.MIMEHeader return err } sc.signature = string(buffer) - return err + + _, err = openpgp.CheckArmoredDetachedSignature(sc.keyring, rawBody, bytes.NewReader(buffer), &sc.config) + + if err != nil { + sc.verified = failed + } else { + sc.verified = ok + } + return nil } return } @@ -116,12 +177,15 @@ func (ac SignatureCollector) GetSignature() string { } -func ParseMIME(mimeBody string) (body *pmmime.BodyCollector, atts, attHeaders []string, err error) { + + +func (openpgp OpenPGP) ParseMIME(mimeBody string, verifierKey []byte) (body *pmmime.BodyCollector, atts, attHeaders []string, err error) { mm, err := mail.ReadMessage(strings.NewReader(mimeBody)) if err != nil { return } + config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: openpgp.getTimeGenerator() } h := textproto.MIMEHeader(mm.Header) mmBodyData, err := ioutil.ReadAll(mm.Body) @@ -130,7 +194,7 @@ func ParseMIME(mimeBody string) (body *pmmime.BodyCollector, atts, attHeaders [] bodyCollector := pmmime.NewBodyCollector(printAccepter) attachmentsCollector := pmmime.NewAttachmentsCollector(bodyCollector) mimeVisitor := pmmime.NewMimeVisitor(attachmentsCollector) - signatureCollector := NewSignatureCollector(mimeVisitor) + signatureCollector := NewSignatureCollector(config, mimeVisitor) err = pmmime.VisitAll(bytes.NewReader(mmBodyData), h, signatureCollector) body = bodyCollector @@ -142,6 +206,83 @@ func ParseMIME(mimeBody string) (body *pmmime.BodyCollector, atts, attHeaders [] /* +func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader, config *packet.Config) (signer *Entity, err error) { + var issuerKeyId uint64 + var hashFunc crypto.Hash + var sigType packet.SignatureType + var keys []Key + var p packet.Packet + + packets := packet.NewReader(signature) + for { + p, err = packets.Next() + if err == io.EOF { + return nil, errors.ErrUnknownIssuer + } + if err != nil { + return nil, err + } + + switch sig := p.(type) { + case *packet.Signature: + if sig.IssuerKeyId == nil { + return nil, errors.StructuralError("signature doesn't have an issuer") + } + issuerKeyId = *sig.IssuerKeyId + hashFunc = sig.Hash + sigType = sig.SigType + case *packet.SignatureV3: + issuerKeyId = sig.IssuerKeyId + hashFunc = sig.Hash + sigType = sig.SigType + default: + return nil, errors.StructuralError("non signature packet found") + } + + keys = keyring.KeysByIdUsage(issuerKeyId, packet.KeyFlagSign) + if len(keys) > 0 { + break + } + } + + if len(keys) == 0 { + panic("unreachable") + } + + h, wrappedHash, err := hashForSignature(hashFunc, sigType) + if err != nil { + return nil, err + } + + if _, err := io.Copy(wrappedHash, signed); err != nil && err != io.EOF { + return nil, err + } + + for _, key := range keys { + switch sig := p.(type) { + case *packet.Signature: + err = key.PublicKey.VerifySignature(h, sig) + if err == nil && sig.KeyExpired(config.Now()) { + err = errors.ErrSignatureExpired + } + case *packet.SignatureV3: + err = key.PublicKey.VerifySignatureV3(h, sig) + default: + panic("unreachable") + } + + if err == errors.ErrSignatureExpired { + return key.Entity, err + } + + if err == nil { + return key.Entity, nil + } + } + + return nil, err +} + // define call back interface type MIMECallbacks interface { onBody(body string, mimetype string) @@ -149,6 +290,9 @@ type MIMECallbacks interface { // Encrypted headers can be an attachment and thus be placed at the end of the mime structure onEncryptedHeaders(headers string) } +func (o *OpenPGP) DecryptMessageVerifyBinKeyPrivbinkeys(encryptedText string, veriferKey []byte, privateKeys []byte, passphrase string, verifyTime int64) (*DecryptSignedVerify, error) { + return o.decryptMessageVerifyAllBin(encryptedText, veriferKey, privateKeys, passphrase, verifyTime) +} func (o *OpenPGP) decryptMIMEMessage(encryptedText string, verifierKey string, privateKeys []byte, passphrase string, callbacks MIMECallbacks, verifyTime int64) (verifier int, err error) {