WIP extract mime body and check signature
This commit is contained in:
parent
dccb3ba50b
commit
34babb3b90
1 changed files with 151 additions and 7 deletions
158
mime.go
158
mime.go
|
|
@ -11,6 +11,10 @@ import (
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"mime"
|
"mime"
|
||||||
"mime/multipart"
|
"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
|
// them as a string
|
||||||
|
|
||||||
type SignatureCollector struct {
|
type SignatureCollector struct {
|
||||||
target pmmime.VisitAcceptor
|
config packet.Config
|
||||||
|
keyring openpgp.KeyRing
|
||||||
|
target pmmime.VisitAcceptor
|
||||||
signature string
|
signature string
|
||||||
|
verified int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSignatureCollector(targetAccepter pmmime.VisitAcceptor) *SignatureCollector {
|
func NewSignatureCollector(config packet.Config, targetAccepter pmmime.VisitAcceptor) *SignatureCollector {
|
||||||
return &SignatureCollector{
|
return &SignatureCollector{
|
||||||
target: targetAccepter,
|
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) {
|
func getMultipartParts(r io.Reader, params map[string]string) (parts []io.Reader, headers []textproto.MIMEHeader, err error) {
|
||||||
mr := multipart.NewReader(r, params["boundary"])
|
mr := multipart.NewReader(r, params["boundary"])
|
||||||
parts = []io.Reader{}
|
parts = []io.Reader{}
|
||||||
|
|
@ -62,12 +101,23 @@ func getMultipartParts(r io.Reader, params map[string]string) (parts []io.Reader
|
||||||
return
|
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) {
|
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"))
|
parentMediaType, params, _ := mime.ParseMediaType(header.Get("Content-Type"))
|
||||||
if parentMediaType == "multipart/signed" {
|
if parentMediaType == "multipart/signed" {
|
||||||
|
newPart, rawBody := getRawMimePart(part, params["boundary"])
|
||||||
var multiparts []io.Reader
|
var multiparts []io.Reader
|
||||||
var multipartHeaders []textproto.MIMEHeader
|
var multipartHeaders []textproto.MIMEHeader
|
||||||
if multiparts, multipartHeaders, err = getMultipartParts(part, params); err != nil {
|
if multiparts, multipartHeaders, err = getMultipartParts(newPart, params); err != nil {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
hasPlainChild := false
|
hasPlainChild := false
|
||||||
|
|
@ -78,7 +128,9 @@ func (sc *SignatureCollector) Accept(part io.Reader, header textproto.MIMEHeader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(multiparts) != 2 {
|
if len(multiparts) != 2 {
|
||||||
|
sc.verified = notSigned
|
||||||
// Invalid multipart/signed format just pass along
|
// Invalid multipart/signed format just pass along
|
||||||
|
ioutil.ReadAll(rawBody)
|
||||||
for i, p := range multiparts {
|
for i, p := range multiparts {
|
||||||
if err = sc.target.Accept(p, multipartHeaders[i], hasPlainChild, true, true); err != nil {
|
if err = sc.target.Accept(p, multipartHeaders[i], hasPlainChild, true, true); err != nil {
|
||||||
return
|
return
|
||||||
|
|
@ -86,13 +138,14 @@ func (sc *SignatureCollector) Accept(part io.Reader, header textproto.MIMEHeader
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// actual multipart/signed format
|
// actual multipart/signed format
|
||||||
err = sc.target.Accept(multiparts[0], multipartHeaders[0], hasPlainChild, true, true)
|
err = sc.target.Accept(multiparts[0], multipartHeaders[0], hasPlainChild, true, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
partData, _ := ioutil.ReadAll(multiparts[1])
|
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)
|
buffer, err := ioutil.ReadAll(decodedPart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -102,7 +155,15 @@ func (sc *SignatureCollector) Accept(part io.Reader, header textproto.MIMEHeader
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sc.signature = string(buffer)
|
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
|
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))
|
mm, err := mail.ReadMessage(strings.NewReader(mimeBody))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: openpgp.getTimeGenerator() }
|
||||||
|
|
||||||
h := textproto.MIMEHeader(mm.Header)
|
h := textproto.MIMEHeader(mm.Header)
|
||||||
mmBodyData, err := ioutil.ReadAll(mm.Body)
|
mmBodyData, err := ioutil.ReadAll(mm.Body)
|
||||||
|
|
@ -130,7 +194,7 @@ func ParseMIME(mimeBody string) (body *pmmime.BodyCollector, atts, attHeaders []
|
||||||
bodyCollector := pmmime.NewBodyCollector(printAccepter)
|
bodyCollector := pmmime.NewBodyCollector(printAccepter)
|
||||||
attachmentsCollector := pmmime.NewAttachmentsCollector(bodyCollector)
|
attachmentsCollector := pmmime.NewAttachmentsCollector(bodyCollector)
|
||||||
mimeVisitor := pmmime.NewMimeVisitor(attachmentsCollector)
|
mimeVisitor := pmmime.NewMimeVisitor(attachmentsCollector)
|
||||||
signatureCollector := NewSignatureCollector(mimeVisitor)
|
signatureCollector := NewSignatureCollector(config, mimeVisitor)
|
||||||
err = pmmime.VisitAll(bytes.NewReader(mmBodyData), h, signatureCollector)
|
err = pmmime.VisitAll(bytes.NewReader(mmBodyData), h, signatureCollector)
|
||||||
|
|
||||||
body = bodyCollector
|
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
|
// define call back interface
|
||||||
type MIMECallbacks interface {
|
type MIMECallbacks interface {
|
||||||
onBody(body string, mimetype string)
|
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
|
// Encrypted headers can be an attachment and thus be placed at the end of the mime structure
|
||||||
onEncryptedHeaders(headers string)
|
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,
|
func (o *OpenPGP) decryptMIMEMessage(encryptedText string, verifierKey string, privateKeys []byte,
|
||||||
passphrase string, callbacks MIMECallbacks, verifyTime int64) (verifier int, err error) {
|
passphrase string, callbacks MIMECallbacks, verifyTime int64) (verifier int, err error) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue