go-gpgmime/sign.go

126 lines
2.3 KiB
Go

package pgpmime
import (
"bytes"
"crypto"
"io"
"mime"
"mime/multipart"
"net/textproto"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/packet"
)
func hashName(h crypto.Hash) string {
switch h {
case crypto.MD5:
return "md5"
case crypto.SHA1:
return "sha1"
case crypto.RIPEMD160:
return "ripemd160"
case crypto.SHA224:
return "sha224"
case crypto.SHA256:
return "sha256"
case crypto.SHA384:
return "sha384"
case crypto.SHA512:
return "sha512"
default:
panic("pgpmime: unknown hash algorithm")
}
}
type signWriter struct {
multipart *multipart.Writer
body io.WriteCloser
signature <-chan io.Reader
h textproto.MIMEHeader
signer *openpgp.Entity
config *packet.Config
}
func (sw *signWriter) open() error {
w, err := sw.multipart.CreatePart(sw.h)
if err != nil {
return err
}
pr, pw := io.Pipe()
ch := make(chan io.Reader, 1)
sw.signature = ch
go func() {
var b bytes.Buffer
err := openpgp.ArmoredDetachSign(&b, sw.signer, pr, sw.config)
pr.CloseWithError(err)
ch <- &b
}()
sw.body = struct {
io.Writer
io.Closer
}{
io.MultiWriter(w, pw),
pw,
}
return nil
}
func (sw *signWriter) Write(b []byte) (n int, err error) {
if sw.body == nil {
if err := sw.open(); err != nil {
return 0, err
}
}
return sw.body.Write(b)
}
func (sw *signWriter) Close() error {
if sw.body == nil {
if err := sw.open(); err != nil {
return err
}
}
if err := sw.body.Close(); err != nil {
return err
}
sig := <-sw.signature
// Create signature part
h := make(textproto.MIMEHeader)
h.Add("Content-Type", "application/pgp-signature")
w, err := sw.multipart.CreatePart(h)
if err != nil {
return err
}
if _, err := io.Copy(w, sig); err != nil {
return err
}
return sw.multipart.Close()
}
func (sw *signWriter) ContentType() string {
return mime.FormatMediaType("multipart/signed", map[string]string{
"boundary": sw.multipart.Boundary(),
"micalg": "pgp-" + hashName(sw.config.Hash()),
"protocol": "application/pgp-signature",
})
}
// Sign creates a new signed PGP/MIME message writer.
func Sign(w io.Writer, h textproto.MIMEHeader, signer *openpgp.Entity, config *packet.Config) (message Writer) {
return &signWriter{
multipart: multipart.NewWriter(w),
h: h,
signer: signer,
config: config,
}
}