passforios-gopenpgp/crypto/attachment.go
Aron Wussler 78e3abb0d8 go vet and lint
* Naming
    * If this is not some OpenPGP standard I follow rule that `DES` should be
      upper case as it is abreviation and `Triple` should be camel-case as it
      is normal word hence `TripleDES`
    * rename `errors2` -> `errorsPGP`
* long lines
    * https://github.com/golang/go/wiki/CodeReviewComments#line-length
    * I bit improved long lines based on my folding
    * reuse type in definition if possible i.e. `a string, b string, c string` -> `a,b,c string`
    * `if long_statetent(); err!=nil {` -> `long_statement;↵ if err!=nil {`
    * spaces around operators (e.g. `a + b` -> `a+b`)
* removing empty lines on start and end of scope
* comments
    * on all exported functions
    * start with function name
* import:
    * order in alphabet
    * separate native, golang.org/x/ and our libs
2019-05-14 14:42:38 +00:00

161 lines
4.1 KiB
Go

package crypto
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"runtime"
"sync"
armorUtils "github.com/ProtonMail/go-pm-crypto/armor"
"github.com/ProtonMail/go-pm-crypto/constants"
"github.com/ProtonMail/go-pm-crypto/models"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/packet"
)
// AttachmentProcessor to encrypt an attachment
type AttachmentProcessor struct {
w *io.WriteCloser
pipe *io.PipeWriter
done sync.WaitGroup
split *models.EncryptedSplit
garbageCollector int
err error
}
// Process allows the attachment processor to write the encrypted attachment
func (ap *AttachmentProcessor) Process(plainData []byte) {
if _, err := (*ap.w).Write(plainData); err != nil {
panic(err)
}
}
// Finish attachment process
func (ap *AttachmentProcessor) Finish() (*models.EncryptedSplit, error) {
if ap.err != nil {
return nil, ap.err
}
(*ap.w).Close()
(*ap.pipe).Close()
ap.done.Wait()
if ap.garbageCollector > 0 {
runtime.GC()
}
return ap.split, nil
}
// encryptAttachment takes input data from file
func (pm *PmCrypto) encryptAttachment(
estimatedSize int, fileName string, publicKey *KeyRing, garbageCollector int,
) (*AttachmentProcessor, error) {
attachmentProc := &AttachmentProcessor{}
// you can also add these one at a time if you need to
attachmentProc.done.Add(1)
attachmentProc.garbageCollector = garbageCollector
hints := &openpgp.FileHints{
FileName: fileName,
}
config := &packet.Config{
DefaultCipher: packet.CipherAES256,
Time: pm.getTimeGenerator(),
}
reader, writer := io.Pipe()
go func() {
defer attachmentProc.done.Done()
split, splitError := SeparateKeyAndData(nil, reader, estimatedSize, garbageCollector)
if attachmentProc.err != nil {
attachmentProc.err = splitError
}
split.Algo = constants.AES256
attachmentProc.split = split
}()
var ew io.WriteCloser
var encryptErr error
ew, encryptErr = openpgp.Encrypt(writer, publicKey.entities, nil, hints, config)
if encryptErr != nil {
return nil, encryptErr
}
attachmentProc.w = &ew
attachmentProc.pipe = writer
return attachmentProc, nil
}
// EncryptAttachment encrypts attachment. Takes input data and key data in
// binary form
func (pm *PmCrypto) EncryptAttachment(
plainData []byte, fileName string, publicKey *KeyRing,
) (*models.EncryptedSplit, error) {
ap, err := pm.encryptAttachment(len(plainData), fileName, publicKey, -1)
if err != nil {
return nil, err
}
ap.Process(plainData)
split, err := ap.Finish()
if err != nil {
return nil, err
}
return split, nil
}
// EncryptAttachmentLowMemory with garbage collected every megabyte
func (pm *PmCrypto) EncryptAttachmentLowMemory(
estimatedSize int, fileName string, publicKey *KeyRing,
) (*AttachmentProcessor, error) {
return pm.encryptAttachment(estimatedSize, fileName, publicKey, 1<<20)
}
// SplitArmor is a Helper method. Splits armored pgp session into key and packet data
func SplitArmor(encrypted string) (*models.EncryptedSplit, error) {
var err error
encryptedRaw, err := armorUtils.Unarmor(encrypted)
if err != nil {
return nil, err
}
encryptedReader := bytes.NewReader(encryptedRaw)
return SeparateKeyAndData(nil, encryptedReader, len(encrypted), -1)
}
// DecryptAttachment takes input data and key data in binary form. The
// privateKeys can contains more keys. The passphrase is used to unlock keys
func (pm *PmCrypto) DecryptAttachment(
keyPacket, dataPacket []byte,
kr *KeyRing, passphrase string,
) ([]byte, error) {
privKeyEntries := kr.entities
if err := kr.Unlock([]byte(passphrase)); err != nil {
err = fmt.Errorf("pm-crypto: cannot decrypt attachment: %v", err)
return nil, err
}
keyReader := bytes.NewReader(keyPacket)
dataReader := bytes.NewReader(dataPacket)
encryptedReader := io.MultiReader(keyReader, dataReader)
config := &packet.Config{Time: pm.getTimeGenerator()}
md, err := openpgp.ReadMessage(encryptedReader, privKeyEntries, nil, config)
if err != nil {
return nil, err
}
decrypted := md.UnverifiedBody
b, err := ioutil.ReadAll(decrypted)
if err != nil {
return nil, err
}
return b, nil
}