passforios-gopenpgp/signature_collector.go

162 lines
4 KiB
Go
Raw Normal View History

2018-09-05 14:56:06 +02:00
package pmcrypto
import (
"io"
"net/textproto"
"proton/pmmime"
"golang.org/x/crypto/openpgp/packet"
"golang.org/x/crypto/openpgp"
"io/ioutil"
"bytes"
"bufio"
"mime/multipart"
"mime"
)
type SignatureCollector struct {
config *packet.Config
keyring openpgp.KeyRing
target pmmime.VisitAcceptor
signature string
verified int
}
func NewSignatureCollector(targetAccepter pmmime.VisitAcceptor, keyring openpgp.KeyRing, config *packet.Config) *SignatureCollector {
return &SignatureCollector{
target: targetAccepter,
config: config,
keyring: keyring,
}
}
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, isPrefix, err := reader.ReadLine()
if err != nil {
return tee, bytes.NewReader(bodyBuffer.Bytes())
}
if bytes.HasPrefix(line, byteBoundary) {
break
}
bodyBuffer.Write(line)
if !isPrefix {
reader.UnreadByte()
reader.UnreadByte()
token, _ := reader.ReadByte()
if token == '\r' {
bodyBuffer.WriteByte(token)
}
bodyBuffer.WriteByte(token)
}
}
ioutil.ReadAll(reader)
data := bodyBuffer.Bytes()
return tee, bytes.NewReader(data[0:len(data) - 1])
}
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{}
headers = []textproto.MIMEHeader{}
var p *multipart.Part
for {
p, err = mr.NextPart()
if err == io.EOF {
err = nil
break
}
if err != nil {
return
}
b, _ := ioutil.ReadAll(p)
buffer := bytes.NewBuffer(b)
parts = append(parts, buffer)
headers = append(headers, p.Header)
}
return
}
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(newPart, params); err != nil {
return
} else {
hasPlainChild := false
for _, header := range multipartHeaders {
mediaType, _, _ := mime.ParseMediaType(header.Get("Content-Type"))
if mediaType == "text/plain" {
hasPlainChild = true
}
}
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
}
}
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), multipartHeaders[1].Get("Content-Transfer-Encoding"))
buffer, err := ioutil.ReadAll(decodedPart)
if err != nil {
return err
}
buffer, err = pmmime.DecodeCharset(buffer, params)
if err != nil {
return err
}
sc.signature = string(buffer)
if sc.keyring != nil {
_, err = openpgp.CheckArmoredDetachedSignature(sc.keyring, rawBody, bytes.NewReader(buffer), sc.config)
if err != nil {
sc.verified = failed
} else {
sc.verified = ok
}
} else {
sc.verified = noVerifier
}
return nil
}
return
}
sc.target.Accept(part, header, hasPlainSibling, isFirst, isLast)
return nil
}
func (ac SignatureCollector) GetSignature() string {
return ac.signature
}