From e40281a273a7907f8cf47ec18026bca4c6263ed2 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 6 Jun 2016 22:55:15 +0200 Subject: [PATCH] First commit --- encrypted.go | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++ message.go | 32 ++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 encrypted.go create mode 100644 message.go diff --git a/encrypted.go b/encrypted.go new file mode 100644 index 0000000..de44b11 --- /dev/null +++ b/encrypted.go @@ -0,0 +1,102 @@ +// Implements MIME security with OpenPGP, as defined in RFC 3156. +package pgpmime + +import ( + "io" + "mime/multipart" + "net/textproto" + + "golang.org/x/crypto/openpgp" +) + +// A PGP/MIME encrypter. +type Encrypter struct { + multipart *multipart.Writer + armored io.WriteCloser + encrypted io.WriteCloser + + to []*openpgp.Entity + signed *openpgp.Entity + + opened bool +} + +// Write control information and create encrypted part. +func (ew *Encrypter) open() (err error) { + // Create control information + h := make(textproto.MIMEHeader) + h.Add("Content-Type", "application/pgp-encrypted") + hw, err := ew.multipart.CreatePart(h) + if err != nil { + return + } + if _, err = io.WriteString(hw, "Version: 1\r\n"); err != nil { + return + } + + // Create body part + h = make(textproto.MIMEHeader) + h.Add("Content-Type", "application/octet-stream") + h.Add("Content-Disposition", "inline") + bw, err := ew.multipart.CreatePart(h) + if err != nil { + return + } + + // Create encrypted part + if ew.armored, err = EncodeArmoredMessage(bw); err != nil { + return + } + if ew.encrypted, err = openpgp.Encrypt(ew.armored, ew.to, ew.signed, nil, nil); err != nil { + return + } + + return +} + +// Write encrypted data. +func (ew *Encrypter) Write(b []byte) (n int, err error) { + // Make sure parts required at the begining of the message have been written + if !ew.opened { + if err = ew.open(); err != nil { + return + } + ew.opened = true + } + + return ew.encrypted.Write(b) +} + +// Finish the PGP/MIME message. +func (ew *Encrypter) Close() (err error) { + if !ew.opened { + if err = ew.open(); err != nil { + return + } + ew.opened = true + } + + if err = ew.encrypted.Close(); err != nil { + return + } + if err = ew.armored.Close(); err != nil { + return + } + err = ew.multipart.Close() + return +} + +// Get the Content-Type of this PGP/MIME message. +func (ew *Encrypter) ContentType() string { + return "multipart/encrypted; boundary=" + ew.multipart.Boundary() + "; protocol=\"application/pgp-encrypted\"" +} + +// Create a new PGP/MIME encrypter. +func NewEncrypter(w io.Writer, to []*openpgp.Entity, signed *openpgp.Entity) *Encrypter { + return &Encrypter{ + multipart: multipart.NewWriter(w), + + to: to, + signed: signed, + } +} diff --git a/message.go b/message.go new file mode 100644 index 0000000..81dd480 --- /dev/null +++ b/message.go @@ -0,0 +1,32 @@ +package pgpmime + +import ( + "errors" + "io" + + "golang.org/x/crypto/openpgp/armor" +) + +// Armored type for PGP encrypted messages. +const MessageType = "PGP MESSAGE" + +// Encode a PGP message armor. +func EncodeArmoredMessage(w io.Writer) (io.WriteCloser, error) { + return armor.Encode(w, MessageType, nil) +} + +// Decode an armored PGP message. +func DecodeArmoredMessage(in io.Reader) (out io.Reader, err error) { + block, err := armor.Decode(in) + if err != nil { + return + } + + if block.Type != MessageType { + err = errors.New("Not an armored PGP message") + return + } + + out = block.Body + return +}