diff --git a/.golangci.yml b/.golangci.yml index b26696e..72182ea 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -29,3 +29,4 @@ linters: - gofumpt # Enforce a stricter format than gofmt - gci # Enforce blank lines check - nlreturn # Enforce blank lines for return statements + - exhaustivestruct # Enforce structures to be fully filled on instantiation - terrible with openpgp configs diff --git a/CHANGELOG.md b/CHANGELOG.md index f4d45e7..3db1a5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -97,6 +97,7 @@ NewPlainMessageFromFile(data []byte, filename string, modTime int) *PlainMessage now accept private keys as public keys and perform automatic casting if the keys are locked. - The `PlainMessage` struct now contains the fields `Filename` (string) and `Time` (uint32) - All the Decrypt* functions return the filename, type, and time specified in the encrypted message +- Improved error wrapping and management ### Fixed - Public key armoring headers diff --git a/armor/armor.go b/armor/armor.go index 1af945e..e9b4a85 100644 --- a/armor/armor.go +++ b/armor/armor.go @@ -9,7 +9,7 @@ import ( "github.com/ProtonMail/gopenpgp/v2/constants" "github.com/ProtonMail/gopenpgp/v2/internal" - + "github.com/pkg/errors" "golang.org/x/crypto/openpgp/armor" ) @@ -46,7 +46,7 @@ func ArmorWithTypeAndCustomHeaders(input []byte, armorType, version, comment str func Unarmor(input string) ([]byte, error) { b, err := internal.Unarmor(input) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopengp: unable to unarmor") } return ioutil.ReadAll(b.Body) } @@ -57,13 +57,13 @@ func armorWithTypeAndHeaders(input []byte, armorType string, headers map[string] w, err := armor.Encode(&b, armorType, headers) if err != nil { - return "", err + return "", errors.Wrap(err, "gopengp: unable to encode armoring") } if _, err = w.Write(input); err != nil { - return "", err + return "", errors.Wrap(err, "gopengp: unable to write armored to buffer") } if err := w.Close(); err != nil { - return "", err + return "", errors.Wrap(err, "gopengp: unable to close armor buffer") } return b.String(), nil } diff --git a/crypto/attachment.go b/crypto/attachment.go index 6c6034c..0de4f36 100644 --- a/crypto/attachment.go +++ b/crypto/attachment.go @@ -8,6 +8,7 @@ import ( "sync" "time" + "github.com/pkg/errors" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/packet" ) @@ -36,11 +37,11 @@ func (ap *AttachmentProcessor) Finish() (*PGPSplitMessage, error) { return nil, ap.err } if err := (*ap.w).Close(); err != nil { - return nil, err + return nil, errors.Wrap(err, "gopengpp: unable to close writer") } if err := (*ap.pipe).Close(); err != nil { - return nil, err + return nil, errors.Wrap(err, "gopengpp: unable to close pipe") } ap.done.Wait() @@ -88,7 +89,7 @@ func (keyRing *KeyRing) newAttachmentProcessor( var encryptErr error ew, encryptErr = openpgp.Encrypt(writer, keyRing.entities, nil, hints, config) if encryptErr != nil { - return nil, encryptErr + return nil, errors.Wrap(encryptErr, "gopengpp: unable to encrypt attachment") } attachmentProc.w = &ew attachmentProc.pipe = writer @@ -148,13 +149,13 @@ func (keyRing *KeyRing) DecryptAttachment(message *PGPSplitMessage) (*PlainMessa md, err := openpgp.ReadMessage(encryptedReader, privKeyEntries, nil, config) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopengpp: unable to read attachment") } decrypted := md.UnverifiedBody b, err := ioutil.ReadAll(decrypted) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopengpp: unable to read attachment body") } return &PlainMessage{ diff --git a/crypto/key.go b/crypto/key.go index f135fd6..5e2872a 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -186,7 +186,11 @@ func (key *Key) Serialize() ([]byte, error) { err = key.entity.SerializePrivateWithoutSigning(&buffer, nil) } - return buffer.Bytes(), err + if err != nil { + return nil, errors.Wrap(err, "gopenpgp: error in serializing key") + } + + return buffer.Bytes(), nil } // Armor returns the armored key as a string with default gopenpgp headers. @@ -235,7 +239,7 @@ func (key *Key) GetArmoredPublicKeyWithCustomHeaders(comment, version string) (s func (key *Key) GetPublicKey() (b []byte, err error) { var outBuf bytes.Buffer if err = key.entity.Serialize(&outBuf); err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in serializing public key") } return outBuf.Bytes(), nil @@ -397,7 +401,7 @@ func (key *Key) readFrom(r io.Reader, armored bool) error { entities, err = openpgp.ReadKeyRing(r) } if err != nil { - return err + return errors.Wrap(err, "gopenpgp: error in reading key ring") } if len(entities) > 1 { @@ -456,7 +460,7 @@ func generateKey( newEntity, err := openpgp.NewEntity(name, comments, email, cfg) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopengpp: error in encoding new entity") } if newEntity.PrivateKey == nil { diff --git a/crypto/keyring.go b/crypto/keyring.go index 19395b7..85399f7 100644 --- a/crypto/keyring.go +++ b/crypto/keyring.go @@ -82,8 +82,7 @@ func (keyRing *KeyRing) getSigningEntity() (*openpgp.Entity, error) { } } if signEntity == nil { - err := errors.New("gopenpgp: cannot sign message, unable to unlock signer key") - return signEntity, err + return nil, errors.New("gopenpgp: cannot sign message, unable to unlock signer key") } return signEntity, nil diff --git a/crypto/keyring_message.go b/crypto/keyring_message.go index 6a7dbf5..363ea98 100644 --- a/crypto/keyring_message.go +++ b/crypto/keyring_message.go @@ -6,6 +6,7 @@ import ( "io" "io/ioutil" + "github.com/pkg/errors" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/packet" ) @@ -47,7 +48,7 @@ func (keyRing *KeyRing) SignDetached(message *PlainMessage) (*PGPSignature, erro var outBuf bytes.Buffer // sign bin if err := openpgp.DetachSign(&outBuf, signEntity, message.NewReader(), config); err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in signing") } return NewPGPSignature(outBuf.Bytes()), nil @@ -95,17 +96,17 @@ func asymmetricEncrypt(plainMessage *PlainMessage, publicKey, privateKey *KeyRin encryptWriter, err = openpgp.EncryptText(&outBuf, publicKey.entities, signEntity, hints, config) } if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in encrypting asymmetrically") } _, err = encryptWriter.Write(plainMessage.GetBinary()) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in writing to message") } err = encryptWriter.Close() if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in closing message") } return outBuf.Bytes(), nil @@ -130,12 +131,12 @@ func asymmetricDecrypt( messageDetails, err := openpgp.ReadMessage(encryptedIO, privKeyEntries, nil, config) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in reading message") } body, err := ioutil.ReadAll(messageDetails.UnverifiedBody) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in reading message body") } if verifyKey != nil { diff --git a/crypto/keyring_session.go b/crypto/keyring_session.go index 06e33eb..1f0a2b4 100644 --- a/crypto/keyring_session.go +++ b/crypto/keyring_session.go @@ -2,7 +2,6 @@ package crypto import ( "bytes" - "fmt" "strconv" "github.com/pkg/errors" @@ -18,7 +17,7 @@ func (keyRing *KeyRing) DecryptSessionKey(keyPacket []byte) (*SessionKey, error) var p packet.Packet var err error if p, err = packets.Next(); err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in reading packets") } ek := p.(*packet.EncryptedKey) @@ -35,7 +34,7 @@ func (keyRing *KeyRing) DecryptSessionKey(keyPacket []byte) (*SessionKey, error) } if decryptErr != nil { - return nil, decryptErr + return nil, errors.Wrap(decryptErr, "gopenpgp: error in decrypting") } if ek == nil { @@ -68,8 +67,7 @@ func (keyRing *KeyRing) EncryptSessionKey(sk *SessionKey) ([]byte, error) { for _, pub := range pubKeys { if err := packet.SerializeEncryptedKey(outbuf, pub, cf, sk.Key, nil); err != nil { - err = fmt.Errorf("gopenpgp: cannot set key: %v", err) - return nil, err + return nil, errors.Wrap(err, "gopenpgp: cannot set key") } } return outbuf.Bytes(), nil diff --git a/crypto/message.go b/crypto/message.go index 1677627..94ec113 100644 --- a/crypto/message.go +++ b/crypto/message.go @@ -3,8 +3,7 @@ package crypto import ( "bytes" "encoding/base64" - "errors" - "fmt" + goerrors "errors" "io" "io/ioutil" "regexp" @@ -15,7 +14,7 @@ import ( "github.com/ProtonMail/gopenpgp/v2/armor" "github.com/ProtonMail/gopenpgp/v2/constants" "github.com/ProtonMail/gopenpgp/v2/internal" - + "github.com/pkg/errors" "golang.org/x/crypto/openpgp/clearsign" "golang.org/x/crypto/openpgp/packet" ) @@ -68,6 +67,7 @@ func NewPlainMessage(data []byte) *PlainMessage { return &PlainMessage{ Data: clone(data), TextType: false, + Filename: "", Time: uint32(GetUnixTime()), } } @@ -90,6 +90,7 @@ func NewPlainMessageFromString(text string) *PlainMessage { return &PlainMessage{ Data: []byte(strings.ReplaceAll(strings.ReplaceAll(text, "\r\n", "\n"), "\n", "\r\n")), TextType: true, + Filename: "", Time: uint32(GetUnixTime()), } } @@ -105,12 +106,12 @@ func NewPGPMessage(data []byte) *PGPMessage { func NewPGPMessageFromArmored(armored string) (*PGPMessage, error) { encryptedIO, err := internal.Unarmor(armored) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in unarmoring message") } message, err := ioutil.ReadAll(encryptedIO.Body) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in reading armored message") } return &PGPMessage{ @@ -150,12 +151,12 @@ func NewPGPSignature(data []byte) *PGPSignature { func NewPGPSignatureFromArmored(armored string) (*PGPSignature, error) { encryptedIO, err := internal.Unarmor(armored) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in unarmoring signature") } signature, err := ioutil.ReadAll(encryptedIO.Body) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in reading armored signature") } return &PGPSignature{ @@ -182,7 +183,7 @@ func NewClearTextMessageFromArmored(signedMessage string) (*ClearTextMessage, er signature, err := ioutil.ReadAll(modulusBlock.ArmoredSignature.Body) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in reading cleartext message") } return NewClearTextMessage(modulusBlock.Bytes, signature), nil @@ -257,7 +258,7 @@ func (msg *PGPMessage) GetEncryptionKeyIDs() ([]uint64, bool) { Loop: for { var p packet.Packet - if p, err = packets.Next(); err == io.EOF { + if p, err = packets.Next(); goerrors.Is(err, io.EOF) { break } switch p := p.(type) { @@ -333,7 +334,7 @@ func (msg *PGPMessage) SeparateKeyAndData(estimatedLength, garbageCollector int) var encryptedKey *packet.EncryptedKey for { var p packet.Packet - if p, err = packets.Next(); err == io.EOF { + if p, err = packets.Next(); goerrors.Is(err, io.EOF) { err = nil break } @@ -354,22 +355,22 @@ func (msg *PGPMessage) SeparateKeyAndData(estimatedLength, garbageCollector int) b.Grow(1<<16 + estimatedLength) // empty encoded length + start byte if _, err := b.Write(make([]byte, 6)); err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in writing data packet header") } if err := b.WriteByte(byte(1)); err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in writing data packet header") } actualLength := 1 block := make([]byte, 128) for { n, err := p.Contents.Read(block) - if err == io.EOF { + if goerrors.Is(err, io.EOF) { break } if _, err := b.Write(block[:n]); err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in writing data packet body") } actualLength += n gcCounter += n @@ -409,7 +410,7 @@ func (msg *PGPMessage) SeparateKeyAndData(estimatedLength, garbageCollector int) var buf bytes.Buffer if err := encryptedKey.Serialize(&buf); err != nil { - return nil, fmt.Errorf("gopenpgp: cannot serialize encrypted key: %v", err) + return nil, errors.Wrap(err, "gopenpgp: cannot serialize encrypted key") } outSplit.KeyPacket = buf.Bytes() @@ -456,7 +457,7 @@ func (msg *ClearTextMessage) GetBinarySignature() []byte { func (msg *ClearTextMessage) GetArmored() (string, error) { armSignature, err := armor.ArmorWithType(msg.GetBinarySignature(), constants.PGPSignatureHeader) if err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: error in armoring cleartext message") } str := "-----BEGIN PGP SIGNED MESSAGE-----\r\nHash: SHA512\r\n\r\n" @@ -486,7 +487,7 @@ func getSignatureKeyIDs(data []byte) ([]uint64, bool) { Loop: for { var p packet.Packet - if p, err = packets.Next(); err == io.EOF { + if p, err = packets.Next(); goerrors.Is(err, io.EOF) { break } switch p := p.(type) { diff --git a/crypto/message_test.go b/crypto/message_test.go index b3134f2..997c35a 100644 --- a/crypto/message_test.go +++ b/crypto/message_test.go @@ -3,6 +3,7 @@ package crypto import ( "bytes" "encoding/base64" + "errors" "io" "testing" "time" @@ -24,7 +25,7 @@ func TestTextMessageEncryptionWithPassword(t *testing.T) { for { var p packet.Packet var errEOF error - if p, errEOF = packets.Next(); errEOF == io.EOF { + if p, errEOF = packets.Next(); errors.Is(errEOF, io.EOF) { break } sessionKey, ok := p.(*packet.SymmetricKeyEncrypted) @@ -138,7 +139,7 @@ func TestIssue11(t *testing.T) { issue11Keyring, err := NewKeyRing(issue11Key) if err != nil { - t.Fatal("Expected no error while bulding private keyring, got:", err) + t.Fatal("Expected no error while building private keyring, got:", err) } senderKey, err := NewKeyFromArmored(readTestFile("issue11_publickey", false)) @@ -154,7 +155,7 @@ func TestIssue11(t *testing.T) { pgpMessage, err := NewPGPMessageFromArmored(readTestFile("issue11_message", false)) if err != nil { - t.Fatal("Expected no error while unlocking private keyring, got:", err) + t.Fatal("Expected no error while reading ciphertext, got:", err) } plainMessage, err := issue11Keyring.Decrypt(pgpMessage, senderKeyring, 0) diff --git a/crypto/mime.go b/crypto/mime.go index 81c2053..b9f9c04 100644 --- a/crypto/mime.go +++ b/crypto/mime.go @@ -8,7 +8,7 @@ import ( "strings" gomime "github.com/ProtonMail/go-mime" - + "github.com/pkg/errors" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/packet" ) @@ -53,14 +53,14 @@ func parseMIME( ) (*gomime.BodyCollector, []string, []string, error) { mm, err := mail.ReadMessage(strings.NewReader(mimeBody)) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, errors.Wrap(err, "gopenpgp: error in reading message") } config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()} h := textproto.MIMEHeader(mm.Header) mmBodyData, err := ioutil.ReadAll(mm.Body) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, errors.Wrap(err, "gopenpgp: error in reading message body data") } printAccepter := gomime.NewMIMEPrinter() diff --git a/crypto/password.go b/crypto/password.go index 6f835f6..a1f18ae 100644 --- a/crypto/password.go +++ b/crypto/password.go @@ -119,16 +119,16 @@ func passwordEncrypt(message *PlainMessage, password []byte) ([]byte, error) { encryptWriter, err := openpgp.SymmetricallyEncrypt(&outBuf, password, hints, config) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in encrypting message symmetrically") } _, err = encryptWriter.Write(message.GetBinary()) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in writing data to message") } err = encryptWriter.Close() if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in closing writer") } return outBuf.Bytes(), nil @@ -151,13 +151,13 @@ func passwordDecrypt(encryptedIO io.Reader, password []byte) (*PlainMessage, err var emptyKeyRing openpgp.EntityList md, err := openpgp.ReadMessage(encryptedIO, emptyKeyRing, prompt, config) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in reading password protected message") } messageBuf := bytes.NewBuffer(nil) _, err = io.Copy(messageBuf, md.UnverifiedBody) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in reading password protected message body") } return &PlainMessage{ diff --git a/crypto/sessionkey.go b/crypto/sessionkey.go index 5e92e86..b63b116 100644 --- a/crypto/sessionkey.go +++ b/crypto/sessionkey.go @@ -50,7 +50,7 @@ func RandomToken(size int) ([]byte, error) { config := &packet.Config{DefaultCipher: packet.CipherAES256} symKey := make([]byte, size) if _, err := io.ReadFull(config.Random(), symKey); err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in generating random token") } return symKey, nil } @@ -152,12 +152,12 @@ func (sk *SessionKey) Encrypt(message *PlainMessage) ([]byte, error) { _, err = encryptWriter.Write(message.GetBinary()) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in writing message") } err = encryptWriter.Close() if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in closing message") } return encBuf.Bytes(), nil @@ -213,7 +213,7 @@ func (sk *SessionKey) Decrypt(dataPacket []byte) (*PlainMessage, error) { messageBuf := new(bytes.Buffer) _, err = messageBuf.ReadFrom(md.UnverifiedBody) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: error in reading message body") } return &PlainMessage{ @@ -221,7 +221,7 @@ func (sk *SessionKey) Decrypt(dataPacket []byte) (*PlainMessage, error) { TextType: !md.LiteralData.IsBinary, Filename: md.LiteralData.FileName, Time: md.LiteralData.Time, - }, err + }, nil } func (sk *SessionKey) checkSize() error { diff --git a/crypto/signature.go b/crypto/signature.go index 8fb0f34..48f9204 100644 --- a/crypto/signature.go +++ b/crypto/signature.go @@ -3,6 +3,7 @@ package crypto import ( "bytes" "crypto" + "errors" "fmt" "io" "math" @@ -43,8 +44,8 @@ func (e SignatureVerificationError) Error() string { // SignatureFailed. func newSignatureFailed() SignatureVerificationError { return SignatureVerificationError{ - constants.SIGNATURE_FAILED, - "Invalid signature", + Status: constants.SIGNATURE_FAILED, + Message: "Invalid signature", } } @@ -52,8 +53,8 @@ func newSignatureFailed() SignatureVerificationError { // SignatureFailed, with a message describing the signature as insecure. func newSignatureInsecure() SignatureVerificationError { return SignatureVerificationError{ - constants.SIGNATURE_FAILED, - "Insecure signature", + Status: constants.SIGNATURE_FAILED, + Message: "Insecure signature", } } @@ -61,8 +62,8 @@ func newSignatureInsecure() SignatureVerificationError { // SignatureNotSigned. func newSignatureNotSigned() SignatureVerificationError { return SignatureVerificationError{ - constants.SIGNATURE_NOT_SIGNED, - "Missing signature", + Status: constants.SIGNATURE_NOT_SIGNED, + Message: "Missing signature", } } @@ -70,15 +71,15 @@ func newSignatureNotSigned() SignatureVerificationError { // SignatureNoVerifier. func newSignatureNoVerifier() SignatureVerificationError { return SignatureVerificationError{ - constants.SIGNATURE_NO_VERIFIER, - "No matching signature", + Status: constants.SIGNATURE_NO_VERIFIER, + Message: "No matching signature", } } // processSignatureExpiration handles signature time verification manually, so // we can add a margin to the creationTime check. func processSignatureExpiration(md *openpgp.MessageDetails, verifyTime int64) { - if md.SignatureError != pgpErrors.ErrSignatureExpired { + if !errors.Is(md.SignatureError, pgpErrors.ErrSignatureExpired) { return } if verifyTime == 0 { @@ -131,7 +132,7 @@ func verifySignature(pubKeyEntries openpgp.EntityList, origText io.Reader, signa signer, err := openpgp.CheckDetachedSignatureAndHash(pubKeyEntries, origText, signatureReader, allowedHashes, config) - if err == pgpErrors.ErrSignatureExpired && signer != nil && verifyTime > 0 { + if errors.Is(err, pgpErrors.ErrSignatureExpired) && signer != nil && verifyTime > 0 { // if verifyTime = 0: time check disabled, everything is okay // Maybe the creation time offset pushed it over the edge // Retry with the actual verification time diff --git a/crypto/signature_collector.go b/crypto/signature_collector.go index 8b636c0..a6396a6 100644 --- a/crypto/signature_collector.go +++ b/crypto/signature_collector.go @@ -8,7 +8,7 @@ import ( "net/textproto" gomime "github.com/ProtonMail/go-mime" - + "github.com/pkg/errors" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/packet" ) @@ -58,7 +58,7 @@ func (sc *SignatureCollector) Accept( sc.verified = newSignatureNotSigned() // Invalid multipart/signed format just pass along if _, err = ioutil.ReadAll(rawBody); err != nil { - return err + return errors.Wrap(err, "gopenpgp: error in reading raw message body") } for i, p := range multiparts { @@ -72,12 +72,12 @@ func (sc *SignatureCollector) Accept( // actual multipart/signed format err = sc.target.Accept(multiparts[0], multipartHeaders[0], hasPlainChild, true, true) if err != nil { - return err + return errors.Wrap(err, "gopenpgp: error in parsing body") } partData, err := ioutil.ReadAll(multiparts[1]) if err != nil { - return err + return errors.Wrap(err, "gopenpgp: error in ready part data") } decodedPart := gomime.DecodeContentEncoding( @@ -86,12 +86,12 @@ func (sc *SignatureCollector) Accept( buffer, err := ioutil.ReadAll(decodedPart) if err != nil { - return err + return errors.Wrap(err, "gopenpgp: error in reading decoded data") } mediaType, _, _ := mime.ParseMediaType(header.Get("Content-Type")) buffer, err = gomime.DecodeCharset(buffer, mediaType, params) if err != nil { - return err + return errors.Wrap(err, "gopenpgp: error in decoding charset") } sc.signature = string(buffer) str, _ := ioutil.ReadAll(rawBody) @@ -107,7 +107,8 @@ func (sc *SignatureCollector) Accept( } else { sc.verified = newSignatureNoVerifier() } - return err + + return nil } // GetSignature collected by Accept. diff --git a/crypto/signature_test.go b/crypto/signature_test.go index 322d73f..3cf97b9 100644 --- a/crypto/signature_test.go +++ b/crypto/signature_test.go @@ -1,6 +1,7 @@ package crypto import ( + "errors" "regexp" "testing" @@ -44,7 +45,8 @@ func TestVerifyTextDetachedSigWrong(t *testing.T) { assert.EqualError(t, verificationError, "Signature Verification Error: Invalid signature") - err, _ := verificationError.(SignatureVerificationError) + err := &SignatureVerificationError{} + _ = errors.As(verificationError, err) assert.Exactly(t, constants.SIGNATURE_FAILED, err.Status) } diff --git a/helper/cleartext.go b/helper/cleartext.go index 6ddd945..6d81c87 100644 --- a/helper/cleartext.go +++ b/helper/cleartext.go @@ -3,6 +3,7 @@ package helper import ( "github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v2/internal" + "github.com/pkg/errors" ) // SignCleartextMessageArmored signs text given a private key and its @@ -11,18 +12,18 @@ import ( func SignCleartextMessageArmored(privateKey string, passphrase []byte, text string) (string, error) { signingKey, err := crypto.NewKeyFromArmored(privateKey) if err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: error in creating key object") } unlockedKey, err := signingKey.Unlock(passphrase) if err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: error in unlocking key") } defer unlockedKey.ClearPrivateParams() keyRing, err := crypto.NewKeyRing(unlockedKey) if err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: error in creating keyring") } return SignCleartextMessage(keyRing, text) @@ -34,12 +35,12 @@ func SignCleartextMessageArmored(privateKey string, passphrase []byte, text stri func VerifyCleartextMessageArmored(publicKey, armored string, verifyTime int64) (string, error) { signingKey, err := crypto.NewKeyFromArmored(publicKey) if err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: error in creating key object") } verifyKeyRing, err := crypto.NewKeyRing(signingKey) if err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: error in creating key ring") } return VerifyCleartextMessage(verifyKeyRing, armored, verifyTime) @@ -53,7 +54,7 @@ func SignCleartextMessage(keyRing *crypto.KeyRing, text string) (string, error) signature, err := keyRing.SignDetached(message) if err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: error in signing cleartext message") } return crypto.NewClearTextMessage(message.GetBinary(), signature.GetBinary()).GetArmored() @@ -65,14 +66,14 @@ func SignCleartextMessage(keyRing *crypto.KeyRing, text string) (string, error) func VerifyCleartextMessage(keyRing *crypto.KeyRing, armored string, verifyTime int64) (string, error) { clearTextMessage, err := crypto.NewClearTextMessageFromArmored(armored) if err != nil { - return "", err + return "", errors.Wrap(err, "gopengpp: unable to unarmor cleartext message") } message := crypto.NewPlainMessageFromString(clearTextMessage.GetString()) signature := crypto.NewPGPSignature(clearTextMessage.GetBinarySignature()) err = keyRing.VerifyDetached(message, signature, verifyTime) if err != nil { - return "", err + return "", errors.Wrap(err, "gopengpp: unable to verify cleartext message") } return message.GetString(), nil diff --git a/helper/helper.go b/helper/helper.go index 8e0259e..81cc737 100644 --- a/helper/helper.go +++ b/helper/helper.go @@ -2,9 +2,8 @@ package helper import ( - "errors" - "github.com/ProtonMail/gopenpgp/v2/crypto" + "github.com/pkg/errors" ) // EncryptMessageWithPassword encrypts a string with a passphrase using AES256. @@ -14,11 +13,11 @@ func EncryptMessageWithPassword(password []byte, plaintext string) (ciphertext s var message = crypto.NewPlainMessageFromString(plaintext) if pgpMessage, err = crypto.EncryptMessageWithPassword(message, password); err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to encrypt message with password") } if ciphertext, err = pgpMessage.GetArmored(); err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to armor ciphertext") } return ciphertext, nil @@ -31,11 +30,11 @@ func DecryptMessageWithPassword(password []byte, ciphertext string) (plaintext s var pgpMessage *crypto.PGPMessage if pgpMessage, err = crypto.NewPGPMessageFromArmored(ciphertext); err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to unarmor ciphertext") } if message, err = crypto.DecryptMessageWithPassword(pgpMessage, password); err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to decrypt message with password") } return message.GetString(), nil @@ -63,24 +62,24 @@ func EncryptSignMessageArmored( } if privateKeyObj, err = crypto.NewKeyFromArmored(privateKey); err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to read key") } if unlockedKeyObj, err = privateKeyObj.Unlock(passphrase); err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to unlock key") } defer unlockedKeyObj.ClearPrivateParams() if privateKeyRing, err = crypto.NewKeyRing(unlockedKeyObj); err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to create new keyring") } if pgpMessage, err = publicKeyRing.Encrypt(message, privateKeyRing); err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to encrypt message") } if ciphertext, err = pgpMessage.GetArmored(); err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to armor ciphertext") } return ciphertext, nil @@ -116,24 +115,24 @@ func DecryptVerifyMessageArmored( } if privateKeyObj, err = crypto.NewKeyFromArmored(privateKey); err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to unarmor private key") } if unlockedKeyObj, err = privateKeyObj.Unlock(passphrase); err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to unlock private key") } defer unlockedKeyObj.ClearPrivateParams() if privateKeyRing, err = crypto.NewKeyRing(unlockedKeyObj); err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to create new keyring") } if pgpMessage, err = crypto.NewPGPMessageFromArmored(ciphertext); err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to unarmor ciphertext") } if message, err = privateKeyRing.Decrypt(pgpMessage, publicKeyRing, crypto.GetUnixTime()); err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to decrypt message") } return message.GetString(), nil @@ -191,31 +190,31 @@ func encryptSignArmoredDetached( publicKey, privateKey string, passphrase, plainData []byte, ) (ciphertext, encryptedSignature string, err error) { - var message *crypto.PlainMessage = crypto.NewPlainMessage(plainData) + var message = crypto.NewPlainMessage(plainData) // We generate the session key sessionKey, err := crypto.GenerateSessionKey() if err != nil { - return "", "", err + return "", "", errors.Wrap(err, "gopenpgp: unable to generate session key") } // We encrypt the message with the session key messageDataPacket, err := sessionKey.Encrypt(message) if err != nil { - return "", "", err + return "", "", errors.Wrap(err, "gopenpgp: unable to encrypt message") } // We sign the message detachedSignature, err := signDetached(privateKey, passphrase, message) if err != nil { - return "", "", err + return "", "", errors.Wrap(err, "gopenpgp: unable to sign the message") } // We encrypt the signature with the session key signaturePlaintext := crypto.NewPlainMessage(detachedSignature.GetBinary()) signatureDataPacket, err := sessionKey.Encrypt(signaturePlaintext) if err != nil { - return "", "", err + return "", "", errors.Wrap(err, "gopenpgp: unable to encrypt signature") } // We encrypt the session key @@ -227,11 +226,11 @@ func encryptSignArmoredDetached( // We join the key packets and datapackets and armor the message ciphertext, err = crypto.NewPGPSplitMessage(keyPacket, messageDataPacket).GetArmored() if err != nil { - return "", "", err + return "", "", errors.Wrap(err, "gopenpgp: unable to armor message") } encryptedSignature, err = crypto.NewPGPSplitMessage(keyPacket, signatureDataPacket).GetArmored() if err != nil { - return "", "", err + return "", "", errors.Wrap(err, "gopenpgp: unable to armor signature") } return ciphertext, encryptedSignature, nil @@ -323,45 +322,44 @@ func DecryptSessionKey( passphrase, encryptedSessionKey []byte, ) (sessionKey *crypto.SessionKey, err error) { privateKeyObj, err := crypto.NewKeyFromArmored(privateKey) - if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to read armored key") } privateKeyUnlocked, err := privateKeyObj.Unlock(passphrase) - if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to unlock private key") } defer privateKeyUnlocked.ClearPrivateParams() privateKeyRing, err := crypto.NewKeyRing(privateKeyUnlocked) - if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to create new keyring") } + sessionKey, err = privateKeyRing.DecryptSessionKey(encryptedSessionKey) - return + if err != nil { + return nil, errors.Wrap(err, "gopenpgp: unable to decrypt session key") + } + + return sessionKey, nil } func encryptMessageArmored(key string, message *crypto.PlainMessage) (string, error) { publicKeyRing, err := createPublicKeyRing(key) - if err != nil { return "", err } pgpMessage, err := publicKeyRing.Encrypt(message, nil) - if err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to encrypt message") } ciphertext, err := pgpMessage.GetArmored() - if err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to armor message") } return ciphertext, nil @@ -369,35 +367,30 @@ func encryptMessageArmored(key string, message *crypto.PlainMessage) (string, er func decryptMessageArmored(privateKey string, passphrase []byte, ciphertext string) (*crypto.PlainMessage, error) { privateKeyObj, err := crypto.NewKeyFromArmored(privateKey) - if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to parse private key") } privateKeyUnlocked, err := privateKeyObj.Unlock(passphrase) - if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to unlock private key") } defer privateKeyUnlocked.ClearPrivateParams() privateKeyRing, err := crypto.NewKeyRing(privateKeyUnlocked) - if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to create new keyring") } pgpMessage, err := crypto.NewPGPMessageFromArmored(ciphertext) - if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to unarmor ciphertext") } message, err := privateKeyRing.Decrypt(pgpMessage, nil, 0) - if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to decrypt message") } return message, nil @@ -405,29 +398,25 @@ func decryptMessageArmored(privateKey string, passphrase []byte, ciphertext stri func signDetached(privateKey string, passphrase []byte, message *crypto.PlainMessage) (detachedSignature *crypto.PGPSignature, err error) { privateKeyObj, err := crypto.NewKeyFromArmored(privateKey) - if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to parse private key") } privateKeyUnlocked, err := privateKeyObj.Unlock(passphrase) - if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to unlock key") } defer privateKeyUnlocked.ClearPrivateParams() privateKeyRing, err := crypto.NewKeyRing(privateKeyUnlocked) - if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to create new keyring") } detachedSignature, err = privateKeyRing.SignDetached(message) - if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to sign message") } return detachedSignature, nil @@ -438,7 +427,7 @@ func verifyDetachedArmored(publicKey string, message *crypto.PlainMessage, armor // We unarmor the signature if detachedSignature, err = crypto.NewPGPSignatureFromArmored(armoredSignature); err != nil { - return false, err + return false, errors.Wrap(err, "gopenpgp: unable to unarmor signature") } // we verify the signature return verifyDetached(publicKey, message, detachedSignature) @@ -471,40 +460,41 @@ func decryptAttachment( // prepare the private key for decryption if privateKeyObj, err = crypto.NewKeyFromArmored(privateKey); err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to parse private key") } if unlockedKeyObj, err = privateKeyObj.Unlock(passphrase); err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to unlock private key") } defer unlockedKeyObj.ClearPrivateParams() if privateKeyRing, err = crypto.NewKeyRing(unlockedKeyObj); err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to create new keyring") } if message, err = privateKeyRing.DecryptAttachment(packets); err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to decrypt attachment") } return message, nil } -func createPublicKeyRing( - publicKey string, -) (publicKeyRing *crypto.KeyRing, err error) { +func createPublicKeyRing(publicKey string) (*crypto.KeyRing, error) { publicKeyObj, err := crypto.NewKeyFromArmored(publicKey) - if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to parse public key") } if publicKeyObj.IsPrivate() { publicKeyObj, err = publicKeyObj.ToPublic() if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to extract public key from private key") } } - publicKeyRing, err = crypto.NewKeyRing(publicKeyObj) - return + publicKeyRing, err := crypto.NewKeyRing(publicKeyObj) + if err != nil { + return nil, errors.Wrap(err, "gopenpgp: unable to create new keyring") + } + + return publicKeyRing, nil } diff --git a/helper/helper_test.go b/helper/helper_test.go index 1bf518e..7d58dce 100644 --- a/helper/helper_test.go +++ b/helper/helper_test.go @@ -18,7 +18,8 @@ func TestAESEncryption(t *testing.T) { } _, err = DecryptMessageWithPassword([]byte("Wrong passphrase"), ciphertext) - assert.EqualError(t, err, "gopenpgp: wrong password in symmetric decryption") + assert.EqualError(t, err, "gopenpgp: unable to decrypt message with password: "+ + "gopenpgp: error in reading password protected message: gopenpgp: wrong password in symmetric decryption") decrypted, err := DecryptMessageWithPassword(passphrase, ciphertext) if err != nil { @@ -71,7 +72,7 @@ func TestArmoredTextMessageEncryptionVerification(t *testing.T) { testMailboxPassword, // Password defined in base_test armored, ) - assert.EqualError(t, err, "Signature Verification Error: No matching signature") + assert.EqualError(t, err, "gopenpgp: unable to decrypt message: Signature Verification Error: No matching signature") decrypted, err := DecryptVerifyMessageArmored( readTestFile("keyring_privateKey", false), diff --git a/helper/key.go b/helper/key.go index 1ef0a8a..6352545 100644 --- a/helper/key.go +++ b/helper/key.go @@ -2,6 +2,7 @@ package helper import ( "github.com/ProtonMail/gopenpgp/v2/crypto" + "github.com/pkg/errors" ) // UpdatePrivateKeyPassphrase decrypts the given armored privateKey with oldPassphrase, @@ -12,21 +13,26 @@ func UpdatePrivateKeyPassphrase( ) (string, error) { key, err := crypto.NewKeyFromArmored(privateKey) if err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to parse key") } unlocked, err := key.Unlock(oldPassphrase) if err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to unlock old key") } defer unlocked.ClearPrivateParams() locked, err := unlocked.Lock(newPassphrase) if err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to lock new key") } - return locked.Armor() + armored, err := locked.Armor() + if err != nil { + return "", errors.Wrap(err, "gopenpgp: unable to armor new key") + } + + return armored, nil } // GenerateKey generates a key of the given keyType ("rsa" or "x25519"), encrypts it, and returns an armored string. @@ -35,13 +41,13 @@ func UpdatePrivateKeyPassphrase( func GenerateKey(name, email string, passphrase []byte, keyType string, bits int) (string, error) { key, err := crypto.GenerateKey(name, email, keyType, bits) if err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to generate new key") } defer key.ClearPrivateParams() locked, err := key.Lock(passphrase) if err != nil { - return "", err + return "", errors.Wrap(err, "gopenpgp: unable to lock new key") } return locked.Armor() @@ -50,7 +56,7 @@ func GenerateKey(name, email string, passphrase []byte, keyType string, bits int func GetSHA256Fingerprints(publicKey string) ([]string, error) { key, err := crypto.NewKeyFromArmored(publicKey) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to parse key") } return key.GetSHA256Fingerprints(), nil diff --git a/helper/mobile.go b/helper/mobile.go index 03d63e2..d9e0f21 100644 --- a/helper/mobile.go +++ b/helper/mobile.go @@ -2,8 +2,10 @@ package helper import ( "encoding/json" + goerrors "errors" "github.com/ProtonMail/gopenpgp/v2/crypto" + "github.com/pkg/errors" ) type ExplicitVerifyMessage struct { @@ -24,14 +26,15 @@ func DecryptExplicitVerify( message, err := privateKeyRing.Decrypt(pgpMessage, publicKeyRing, verifyTime) if err != nil { - castedErr, isType := err.(crypto.SignatureVerificationError) + castedErr := &crypto.SignatureVerificationError{} + isType := goerrors.As(err, castedErr) if !isType { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to decrypt message") } explicitVerify = &ExplicitVerifyMessage{ Message: message, - SignatureVerificationError: &castedErr, + SignatureVerificationError: castedErr, } } else { explicitVerify = &ExplicitVerifyMessage{ @@ -51,7 +54,7 @@ func DecryptAttachment(keyPacket []byte, dataPacket []byte, keyRing *crypto.KeyR decrypted, err := keyRing.DecryptAttachment(splitMessage) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to decrypt attachment") } return decrypted, nil } @@ -64,7 +67,7 @@ func EncryptAttachment(plainData []byte, filename string, keyRing *crypto.KeyRin plainMessage := crypto.NewPlainMessageFromFile(plainData, filename, uint32(crypto.GetUnixTime())) decrypted, err := keyRing.EncryptAttachment(plainMessage, "") if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to encrypt attachment") } return decrypted, nil } @@ -74,7 +77,7 @@ func EncryptAttachment(plainData []byte, filename string, keyRing *crypto.KeyRin func GetJsonSHA256Fingerprints(publicKey string) ([]byte, error) { key, err := crypto.NewKeyFromArmored(publicKey) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to parse key") } return json.Marshal(key.GetSHA256Fingerprints()) @@ -94,5 +97,9 @@ func EncryptSignArmoredDetachedMobile( if err != nil { return nil, err } - return &EncryptSignArmoredDetachedMobileResult{ciphertext, encryptedSignature}, nil + + return &EncryptSignArmoredDetachedMobileResult{ + Ciphertext: ciphertext, + EncryptedSignature: encryptedSignature, + }, nil } diff --git a/helper/sign_detached.go b/helper/sign_detached.go index a55cd0e..9087392 100644 --- a/helper/sign_detached.go +++ b/helper/sign_detached.go @@ -5,6 +5,7 @@ package helper import ( "github.com/ProtonMail/gopenpgp/v2/crypto" + "github.com/pkg/errors" ) // EncryptSignAttachment encrypts an attachment using a detached signature, given a publicKey, a privateKey @@ -13,46 +14,36 @@ import ( func EncryptSignAttachment( publicKey, privateKey string, passphrase []byte, filename string, plainData []byte, ) (keyPacket, dataPacket, signature []byte, err error) { - var publicKeyObj, privateKeyObj, unlockedKeyObj *crypto.Key + var privateKeyObj, unlockedKeyObj *crypto.Key var publicKeyRing, privateKeyRing *crypto.KeyRing var packets *crypto.PGPSplitMessage var signatureObj *crypto.PGPSignature var binMessage = crypto.NewPlainMessageFromFile(plainData, filename, uint32(crypto.GetUnixTime())) - if publicKeyObj, err = crypto.NewKeyFromArmored(publicKey); err != nil { - return nil, nil, nil, err - } - if publicKeyObj.IsPrivate() { - publicKeyObj, err = publicKeyObj.ToPublic() - if err != nil { - return nil, nil, nil, err - } - } - - if publicKeyRing, err = crypto.NewKeyRing(publicKeyObj); err != nil { + if publicKeyRing, err = createPublicKeyRing(publicKey); err != nil { return nil, nil, nil, err } if privateKeyObj, err = crypto.NewKeyFromArmored(privateKey); err != nil { - return nil, nil, nil, err + return nil, nil, nil, errors.Wrap(err, "gopenpgp: unable to parse private key") } if unlockedKeyObj, err = privateKeyObj.Unlock(passphrase); err != nil { - return nil, nil, nil, err + return nil, nil, nil, errors.Wrap(err, "gopenpgp: unable to unlock key") } defer unlockedKeyObj.ClearPrivateParams() if privateKeyRing, err = crypto.NewKeyRing(unlockedKeyObj); err != nil { - return nil, nil, nil, err + return nil, nil, nil, errors.Wrap(err, "gopenpgp: unable to create private keyring") } if packets, err = publicKeyRing.EncryptAttachment(binMessage, ""); err != nil { - return nil, nil, nil, err + return nil, nil, nil, errors.Wrap(err, "gopenpgp: unable to encrypt attachment") } if signatureObj, err = privateKeyRing.SignDetached(binMessage); err != nil { - return nil, nil, nil, err + return nil, nil, nil, errors.Wrap(err, "gopenpgp: unable to sign attachment") } return packets.GetBinaryKeyPacket(), packets.GetBinaryDataPacket(), signatureObj.GetBinary(), nil diff --git a/internal/armor.go b/internal/armor.go index 1b17df3..43868a5 100644 --- a/internal/armor.go +++ b/internal/armor.go @@ -3,6 +3,7 @@ package internal import ( "strings" + "github.com/pkg/errors" "golang.org/x/crypto/openpgp/armor" ) @@ -11,7 +12,7 @@ func Unarmor(input string) (*armor.Block, error) { io := strings.NewReader(input) b, err := armor.Decode(io) if err != nil { - return nil, err + return nil, errors.Wrap(err, "gopenpgp: unable to armor") } return b, nil }