From ac8a49c1148633d2db78db817b5b8e260a72f173 Mon Sep 17 00:00:00 2001 From: zugzwang Date: Tue, 28 Apr 2020 13:55:36 +0200 Subject: [PATCH] Update lint (#44) * Reduce complexity of SignatureCollector.Accept * Add stylecheck linter, and lint accordingly * Rephrase some comments * godot - Top level comments should end with a dot. * nestif - Reduce nested complexity of code * Review changes Co-authored-by: Aron Wussler --- .golangci.yml | 47 +++--------- CHANGELOG.md | 1 + crypto/attachment.go | 4 +- crypto/key.go | 28 ++++---- crypto/keyring.go | 16 ++--- crypto/keyring_message.go | 15 ++-- crypto/message.go | 64 +++++++++-------- crypto/password.go | 17 ++--- crypto/sessionkey.go | 19 ++--- crypto/signature.go | 101 +++++++++++++------------- crypto/signature_collector.go | 132 ++++++++++++++++------------------ crypto/time.go | 10 +-- helper/cleartext.go | 21 +++--- helper/helper.go | 27 +++---- helper/mobile.go | 15 ++-- 15 files changed, 252 insertions(+), 265 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index c6496d7..251bd2b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -11,44 +11,17 @@ issues: exclude-use-default: false exclude: - Using the variable on range scope `tt` in function literal + - GetJsonSHA256Fingerprints should be GetJSONSHA256Fingerprints + - ST1003 # CamelCase variables; see constants/cipher.go linters: - enable: - - deadcode # Finds unused code [fast: true, auto-fix: false] - - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: true, auto-fix: false] - - gosimple # Linter for Go source code that specializes in simplifying a code [fast: true, auto-fix: false] - - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string [fast: true, auto-fix: false] - - ineffassign # Detects when assignments to existing variables are not used [fast: true, auto-fix: false] - - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks [fast: true, auto-fix: false] - - structcheck # Finds unused struct fields [fast: true, auto-fix: false] - - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code [fast: true, auto-fix: false] - - unused # Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false] - - varcheck # Finds unused global variables and constants [fast: true, auto-fix: false] - - depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false] - - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false] -# - dupl # Tool for code clone detection [fast: true, auto-fix: false] - - funlen # Tool for detection of long functions [fast: true, auto-fix: false] -# - gochecknoglobals # Checks that no globals are present in Go code [fast: true, auto-fix: false] -# - gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false] - - goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false] - - gocritic # The most opinionated Go source code linter [fast: true, auto-fix: false] - - gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false] - - godox # Tool for detection of FIXME, TODO and other comment keywords [fast: true, auto-fix: false] - - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true] - - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports [fast: true, auto-fix: true] -# - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes [fast: true, auto-fix: false] - - gosec # Inspects source code for security problems [fast: true, auto-fix: false] - - interfacer # Linter that suggests narrower interface types [fast: true, auto-fix: false] - - maligned # Tool to detect Go structs that would take less memory if their fields were sorted [fast: true, auto-fix: false] - - misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true] - - nakedret # Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false] - - prealloc # Finds slice declarations that could potentially be preallocated [fast: true, auto-fix: false] - - scopelint # Scopelint checks for unpinned variables in go programs [fast: true, auto-fix: false] -# - stylecheck # Stylecheck is a replacement for golint [fast: true, auto-fix: false] - - unconvert # Remove unnecessary type conversions [fast: true, auto-fix: false] - - unparam # Reports unused function parameters [fast: true, auto-fix: false] - - whitespace # Tool for detection of leading and trailing whitespace [fast: true, auto-fix: true] - + enable-all: true disable: + - dupl # Tool for code clone detection [fast: true, auto-fix: false] + - gochecknoglobals # Checks that no globals are present in Go code [fast: true, auto-fix: false] + - gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false] + - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes [fast: true, auto-fix: false] + - gomnd # An analyzer to detect magic numbers. [fast: true, auto-fix: false] + - lll # Reports long lines [fast: true, auto-fix: false] + - testpackage # Makes you use a separate _test package [fast: true, auto-fix: false] - wsl # Whitespace Linter - Forces you to use empty lines! [fast: true, auto-fix: false] - - lll # Reports long lines [fast: true, auto-fix: false]\ diff --git a/CHANGELOG.md b/CHANGELOG.md index d096970..77c2744 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Changed - Providing empty passphrase does no longer throw an error when unlocking an unencrypted private key +- Improved code linter ### Added - SHA256 fingerprint support diff --git a/crypto/attachment.go b/crypto/attachment.go index 141b765..3e51c0a 100644 --- a/crypto/attachment.go +++ b/crypto/attachment.go @@ -22,14 +22,14 @@ type AttachmentProcessor struct { err error } -// Process writes attachment data to be encrypted +// Process writes attachment data to be encrypted. func (ap *AttachmentProcessor) Process(plainData []byte) { if _, err := (*ap.w).Write(plainData); err != nil { panic(err) } } -// Finish closes the attachment and returns the encrypted data +// Finish closes the attachment and returns the encrypted data. func (ap *AttachmentProcessor) Finish() (*PGPSplitMessage, error) { if ap.err != nil { return nil, ap.err diff --git a/crypto/key.go b/crypto/key.go index 349bed4..f70c164 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -20,7 +20,7 @@ import ( packet "golang.org/x/crypto/openpgp/packet" ) -// Key contains a single private or public key +// Key contains a single private or public key. type Key struct { // PGP entities in this keyring. entity *openpgp.Entity @@ -50,12 +50,12 @@ func NewKeyFromReader(r io.Reader) (key *Key, err error) { return key, nil } -// NewKey creates a new key from the first key in the unarmored binary data +// NewKey creates a new key from the first key in the unarmored binary data. func NewKey(binKeys []byte) (key *Key, err error) { return NewKeyFromReader(bytes.NewReader(clone(binKeys))) } -// NewKeyFromArmored creates a new key from the first key in an armored +// NewKeyFromArmored creates a new key from the first key in an armored string. func NewKeyFromArmored(armored string) (key *Key, err error) { return NewKeyFromArmoredReader(strings.NewReader(armored)) } @@ -132,7 +132,7 @@ func (key *Key) Lock(passphrase []byte) (*Key, error) { return lockedKey, nil } -// Unlock unlocks a copy of the key +// Unlock unlocks a copy of the key. func (key *Key) Unlock(passphrase []byte) (*Key, error) { isLocked, err := key.IsLocked() if err != nil { @@ -234,12 +234,12 @@ func (key *Key) IsExpired() bool { return !ok } -// IsPrivate returns true if the key is private +// IsPrivate returns true if the key is private. func (key *Key) IsPrivate() bool { return key.entity.PrivateKey != nil } -// IsLocked checks if a private key is locked +// IsLocked checks if a private key is locked. func (key *Key) IsLocked() (bool, error) { if key.entity.PrivateKey == nil { return true, errors.New("gopenpgp: a public key cannot be locked") @@ -254,7 +254,7 @@ func (key *Key) IsLocked() (bool, error) { return key.entity.PrivateKey.Encrypted, nil } -// IsUnlocked checks if a private key is unlocked +// IsUnlocked checks if a private key is unlocked. func (key *Key) IsUnlocked() (bool, error) { if key.entity.PrivateKey == nil { return true, errors.New("gopenpgp: a public key cannot be unlocked") @@ -269,7 +269,8 @@ func (key *Key) IsUnlocked() (bool, error) { return !key.entity.PrivateKey.Encrypted, nil } -// Check verifies if the public keys match the private key parameters by signing and verifying +// Check verifies if the public keys match the private key parameters by +// signing and verifying. func (key *Key) Check() (bool, error) { var err error testSign := bytes.Repeat([]byte{0x01}, 64) @@ -314,22 +315,22 @@ func (key *Key) PrintFingerprints() { fmt.Println("PrimaryKey:" + hex.EncodeToString(key.entity.PrimaryKey.Fingerprint[:])) } -// GetHexKeyID returns the key ID, hex encoded as a string +// GetHexKeyID returns the key ID, hex encoded as a string. func (key *Key) GetHexKeyID() string { return strconv.FormatUint(key.GetKeyID(), 16) } -// GetKeyID returns the key ID, encoded as 8-byte int +// GetKeyID returns the key ID, encoded as 8-byte int. func (key *Key) GetKeyID() uint64 { return key.entity.PrimaryKey.KeyId } -// GetFingerprint gets the fingerprint from the key +// GetFingerprint gets the fingerprint from the key. func (key *Key) GetFingerprint() string { return hex.EncodeToString(key.entity.PrimaryKey.Fingerprint[:]) } -// GetSHA256Fingerprints computes the SHA256 fingerprints of the key and subkeys +// GetSHA256Fingerprints computes the SHA256 fingerprints of the key and subkeys. func (key *Key) GetSHA256Fingerprints() (fingerprints []string) { fingerprints = append(fingerprints, hex.EncodeToString(getSHA256FingerprintBytes(key.entity.PrimaryKey))) for _, sub := range key.entity.Subkeys { @@ -340,7 +341,8 @@ func (key *Key) GetSHA256Fingerprints() (fingerprints []string) { // --- Internal methods -// getSHA256FingerprintBytes computes the SHA256 fingerprint of a public key object +// getSHA256FingerprintBytes computes the SHA256 fingerprint of a public key +// object. func getSHA256FingerprintBytes(pk *packet.PublicKey) []byte { fingerPrint := sha256.New() diff --git a/crypto/keyring.go b/crypto/keyring.go index 75fc316..19395b7 100644 --- a/crypto/keyring.go +++ b/crypto/keyring.go @@ -26,7 +26,7 @@ type Identity struct { // --- New keyrings -// NewKeyRing creates a new KeyRing, empty if key is nil +// NewKeyRing creates a new KeyRing, empty if key is nil. func NewKeyRing(key *Key) (*KeyRing, error) { keyRing := &KeyRing{} var err error @@ -36,7 +36,7 @@ func NewKeyRing(key *Key) (*KeyRing, error) { return keyRing, err } -// --- Add keys to keyring +// AddKey adds the given key to the keyring. func (keyRing *KeyRing) AddKey(key *Key) error { if key.IsPrivate() { unlocked, err := key.IsUnlocked() @@ -91,17 +91,17 @@ func (keyRing *KeyRing) getSigningEntity() (*openpgp.Entity, error) { // --- Extract info from key -// CountEntities returns the number of entities in the keyring +// CountEntities returns the number of entities in the keyring. func (keyRing *KeyRing) CountEntities() int { return len(keyRing.entities) } -// CountDecryptionEntities returns the number of entities in the keyring +// CountDecryptionEntities returns the number of entities in the keyring. func (keyRing *KeyRing) CountDecryptionEntities() int { return len(keyRing.entities.DecryptionKeys()) } -// Identities returns the list of identities associated with this key ring. +// GetIdentities returns the list of identities associated with this key ring. func (keyRing *KeyRing) GetIdentities() []*Identity { var identities []*Identity for _, e := range keyRing.entities { @@ -172,7 +172,7 @@ func FilterExpiredKeys(contactKeys []*KeyRing) (filteredKeys []*KeyRing, err err return filteredKeys, nil } -// FirstKey returns a KeyRing with only the first key of the original one +// FirstKey returns a KeyRing with only the first key of the original one. func (keyRing *KeyRing) FirstKey() (*KeyRing, error) { if len(keyRing.entities) == 0 { return nil, errors.New("gopenpgp: No key available in this keyring") @@ -183,7 +183,7 @@ func (keyRing *KeyRing) FirstKey() (*KeyRing, error) { return newKeyRing.Copy() } -// Copy creates a deep copy of the keyring +// Copy creates a deep copy of the keyring. func (keyRing *KeyRing) Copy() (*KeyRing, error) { newKeyRing := &KeyRing{} @@ -223,7 +223,7 @@ func (keyRing *KeyRing) ClearPrivateParams() { // INTERNAL FUNCTIONS -// append appends a key to the keyring +// appendKey appends a key to the keyring. func (keyRing *KeyRing) appendKey(key *Key) { keyRing.entities = append(keyRing.entities, key.entity) } diff --git a/crypto/keyring_message.go b/crypto/keyring_message.go index ba23d21..0a2af8f 100644 --- a/crypto/keyring_message.go +++ b/crypto/keyring_message.go @@ -12,8 +12,8 @@ import ( // Encrypt encrypts a PlainMessage, outputs a PGPMessage. // If an unlocked private key is also provided it will also sign the message. -// * message : The plaintext input as a PlainMessage -// * privateKey : (optional) an unlocked private keyring to include signature in the message +// * message : The plaintext input as a PlainMessage. +// * privateKey : (optional) an unlocked private keyring to include signature in the message. func (keyRing *KeyRing) Encrypt(message *PlainMessage, privateKey *KeyRing) (*PGPMessage, error) { encrypted, err := asymmetricEncrypt(message.GetBinary(), keyRing, privateKey, message.IsBinary()) if err != nil { @@ -28,7 +28,8 @@ func (keyRing *KeyRing) Encrypt(message *PlainMessage, privateKey *KeyRing) (*PG // * verifyKey : Public key for signature verification (optional) // * verifyTime : Time at verification (necessary only if verifyKey is not nil) // -// When verifyKey is not provided, then verifyTime should be zero, and signature verification will be ignored +// When verifyKey is not provided, then verifyTime should be zero, and +// signature verification will be ignored. func (keyRing *KeyRing) Decrypt( message *PGPMessage, verifyKey *KeyRing, verifyTime int64, ) (*PlainMessage, error) { @@ -37,7 +38,7 @@ func (keyRing *KeyRing) Decrypt( return NewPlainMessage(decrypted), err } -// SignDetached generates and returns a PGPSignature for a given PlainMessage +// SignDetached generates and returns a PGPSignature for a given PlainMessage. func (keyRing *KeyRing) SignDetached(message *PlainMessage) (*PGPSignature, error) { signEntity, err := keyRing.getSigningEntity() if err != nil { @@ -55,7 +56,7 @@ func (keyRing *KeyRing) SignDetached(message *PlainMessage) (*PGPSignature, erro } // VerifyDetached verifies a PlainMessage with embedded a PGPSignature -// and returns a SignatureVerificationError if fails +// and returns a SignatureVerificationError if fails. func (keyRing *KeyRing) VerifyDetached(message *PlainMessage, signature *PGPSignature, verifyTime int64) error { return verifySignature( keyRing.entities, @@ -67,7 +68,7 @@ func (keyRing *KeyRing) VerifyDetached(message *PlainMessage, signature *PGPSign // ------ INTERNAL FUNCTIONS ------- -// Core for encryption+signature functions +// Core for encryption+signature functions. func asymmetricEncrypt(data []byte, publicKey *KeyRing, privateKey *KeyRing, isBinary bool) ([]byte, error) { var outBuf bytes.Buffer var encryptWriter io.WriteCloser @@ -111,7 +112,7 @@ func asymmetricEncrypt(data []byte, publicKey *KeyRing, privateKey *KeyRing, isB return outBuf.Bytes(), nil } -// Core for decryption+verification functions +// Core for decryption+verification functions. func asymmetricDecrypt( encryptedIO io.Reader, privateKey *KeyRing, verifyKey *KeyRing, verifyTime int64, ) (plaintext []byte, err error) { diff --git a/crypto/message.go b/crypto/message.go index 10ab8a4..8ecda6f 100644 --- a/crypto/message.go +++ b/crypto/message.go @@ -47,8 +47,7 @@ type PGPSplitMessage struct { KeyPacket []byte } -// ClearTextMessage, split signed clear text message container. -// A Cleartext message is a signed PGP message, that is not encrypted, +// A ClearTextMessage is a signed but not encrypted PGP message, // i.e. the ones beginning with -----BEGIN PGP SIGNED MESSAGE----- type ClearTextMessage struct { Data []byte @@ -126,7 +125,8 @@ func NewPGPSignature(data []byte) *PGPSignature { } } -// NewPGPSignatureFromArmored generates a new PGPSignature from the armored string ready for verification. +// NewPGPSignatureFromArmored generates a new PGPSignature from the armored +// string ready for verification. func NewPGPSignatureFromArmored(armored string) (*PGPSignature, error) { encryptedIO, err := internal.Unarmor(armored) if err != nil { @@ -143,7 +143,8 @@ func NewPGPSignatureFromArmored(armored string) (*PGPSignature, error) { }, nil } -// NewClearTextMessage generates a new ClearTextMessage from data and signature +// NewClearTextMessage generates a new ClearTextMessage from data and +// signature. func NewClearTextMessage(data []byte, signature []byte) *ClearTextMessage { return &ClearTextMessage{ Data: clone(data), @@ -151,7 +152,8 @@ func NewClearTextMessage(data []byte, signature []byte) *ClearTextMessage { } } -// NewClearTextMessageFromArmored returns the message body and unarmored signature from a clearsigned message. +// NewClearTextMessageFromArmored returns the message body and unarmored +// signature from a clearsigned message. func NewClearTextMessageFromArmored(signedMessage string) (*ClearTextMessage, error) { modulusBlock, rest := clearsign.Decode([]byte(signedMessage)) if len(rest) != 0 { @@ -168,79 +170,84 @@ func NewClearTextMessageFromArmored(signedMessage string) (*ClearTextMessage, er // ---- MODEL METHODS ----- -// GetBinary returns the binary content of the message as a []byte +// GetBinary returns the binary content of the message as a []byte. func (msg *PlainMessage) GetBinary() []byte { return msg.Data } -// GetString returns the content of the message as a string +// GetString returns the content of the message as a string. func (msg *PlainMessage) GetString() string { return string(msg.Data) } -// GetBase64 returns the base-64 encoded binary content of the message as a string +// GetBase64 returns the base-64 encoded binary content of the message as a +// string. func (msg *PlainMessage) GetBase64() string { return base64.StdEncoding.EncodeToString(msg.Data) } -// NewReader returns a New io.Reader for the binary data of the message +// NewReader returns a New io.Reader for the binary data of the message. func (msg *PlainMessage) NewReader() io.Reader { return bytes.NewReader(msg.GetBinary()) } -// IsText returns whether the message is a text message +// IsText returns whether the message is a text message. func (msg *PlainMessage) IsText() bool { return msg.TextType } -// IsBinary returns whether the message is a binary message +// IsBinary returns whether the message is a binary message. func (msg *PlainMessage) IsBinary() bool { return !msg.TextType } -// GetBinary returns the unarmored binary content of the message as a []byte +// GetBinary returns the unarmored binary content of the message as a []byte. func (msg *PGPMessage) GetBinary() []byte { return msg.Data } -// NewReader returns a New io.Reader for the unarmored binary data of the message +// NewReader returns a New io.Reader for the unarmored binary data of the +// message. func (msg *PGPMessage) NewReader() io.Reader { return bytes.NewReader(msg.GetBinary()) } -// GetArmored returns the armored message as a string +// GetArmored returns the armored message as a string. func (msg *PGPMessage) GetArmored() (string, error) { return armor.ArmorWithType(msg.Data, constants.PGPMessageHeader) } -// GetBinaryDataPacket returns the unarmored binary datapacket as a []byte +// GetBinaryDataPacket returns the unarmored binary datapacket as a []byte. func (msg *PGPSplitMessage) GetBinaryDataPacket() []byte { return msg.DataPacket } -// GetBinaryKeyPacket returns the unarmored binary keypacket as a []byte +// GetBinaryKeyPacket returns the unarmored binary keypacket as a []byte. func (msg *PGPSplitMessage) GetBinaryKeyPacket() []byte { return msg.KeyPacket } -// GetBinary returns the unarmored binary joined packets as a []byte +// GetBinary returns the unarmored binary joined packets as a []byte. func (msg *PGPSplitMessage) GetBinary() []byte { return append(msg.KeyPacket, msg.DataPacket...) } -// GetArmored returns the armored message as a string, with joined data and key packets +// GetArmored returns the armored message as a string, with joined data and key +// packets. func (msg *PGPSplitMessage) GetArmored() (string, error) { return armor.ArmorWithType(msg.GetBinary(), constants.PGPMessageHeader) } -// GetPGPMessage joins asymmetric session key packet with the symmetric data packet to obtain a PGP message +// GetPGPMessage joins asymmetric session key packet with the symmetric data +// packet to obtain a PGP message. func (msg *PGPSplitMessage) GetPGPMessage() *PGPMessage { return NewPGPMessage(append(msg.KeyPacket, msg.DataPacket...)) } -// SeparateKeyAndData returns the first keypacket and the (hopefully unique) dataPacket (not verified) -// * estimatedLength is the estimate length of the message -// * garbageCollector > 0 activates the garbage collector +// SeparateKeyAndData returns the first keypacket and the (hopefully unique) +// dataPacket (not verified). +// * estimatedLength is the estimate length of the message. +// * garbageCollector > 0 activates the garbage collector. func (msg *PGPMessage) SeparateKeyAndData(estimatedLength, garbageCollector int) (outSplit *PGPSplitMessage, err error) { // For info on each, see: https://golang.org/pkg/runtime/#MemStats packets := packet.NewReader(bytes.NewReader(msg.Data)) @@ -334,32 +341,33 @@ func (msg *PGPMessage) SeparateKeyAndData(estimatedLength, garbageCollector int) return outSplit, nil } -// GetBinary returns the unarmored binary content of the signature as a []byte +// GetBinary returns the unarmored binary content of the signature as a []byte. func (msg *PGPSignature) GetBinary() []byte { return msg.Data } -// GetArmored returns the armored signature as a string +// GetArmored returns the armored signature as a string. func (msg *PGPSignature) GetArmored() (string, error) { return armor.ArmorWithType(msg.Data, constants.PGPSignatureHeader) } -// GetBinary returns the unarmored signed data as a []byte +// GetBinary returns the unarmored signed data as a []byte. func (msg *ClearTextMessage) GetBinary() []byte { return msg.Data } -// GetString returns the unarmored signed data as a string +// GetString returns the unarmored signed data as a string. func (msg *ClearTextMessage) GetString() string { return string(msg.Data) } -// GetBinarySignature returns the unarmored binary signature as a []byte +// GetBinarySignature returns the unarmored binary signature as a []byte. func (msg *ClearTextMessage) GetBinarySignature() []byte { return msg.Signature } -// GetArmored armors plaintext and signature with the PGP SIGNED MESSAGE armoring +// GetArmored armors plaintext and signature with the PGP SIGNED MESSAGE +// armoring. func (msg *ClearTextMessage) GetArmored() (string, error) { armSignature, err := armor.ArmorWithType(msg.GetBinarySignature(), constants.PGPSignatureHeader) if err != nil { diff --git a/crypto/password.go b/crypto/password.go index ea718b6..f85fba2 100644 --- a/crypto/password.go +++ b/crypto/password.go @@ -10,10 +10,11 @@ import ( "github.com/pkg/errors" ) -// Encrypt encrypts a PlainMessage to PGPMessage with a SymmetricKey -// * message : The plain data as a PlainMessage -// * password: A password that will be derived into an encryption key -// * output : The encrypted data as PGPMessage +// EncryptMessageWithPassword encrypts a PlainMessage to PGPMessage with a +// SymmetricKey. +// * message : The plain data as a PlainMessage. +// * password: A password that will be derived into an encryption key. +// * output : The encrypted data as PGPMessage. func EncryptMessageWithPassword(message *PlainMessage, password []byte) (*PGPMessage, error) { encrypted, err := passwordEncrypt(message.GetBinary(), password, message.IsBinary()) if err != nil { @@ -23,10 +24,10 @@ func EncryptMessageWithPassword(message *PlainMessage, password []byte) (*PGPMes return NewPGPMessage(encrypted), nil } -// Decrypt decrypts password protected pgp binary messages -// * encrypted: The encrypted data as PGPMessage -// * password: A password that will be derived into an encryption key -// * output: The decrypted data as PlainMessage +// DecryptMessageWithPassword decrypts password protected pgp binary messages. +// * encrypted: The encrypted data as PGPMessage. +// * password: A password that will be derived into an encryption key. +// * output: The decrypted data as PlainMessage. func DecryptMessageWithPassword(message *PGPMessage, password []byte) (*PlainMessage, error) { decrypted, err := passwordDecrypt(message.NewReader(), password) if err != nil { diff --git a/crypto/sessionkey.go b/crypto/sessionkey.go index 534e426..6751119 100644 --- a/crypto/sessionkey.go +++ b/crypto/sessionkey.go @@ -45,7 +45,7 @@ func (sk *SessionKey) GetBase64Key() string { return base64.StdEncoding.EncodeToString(sk.Key) } -// RandomToken generates a random token with the specified key size +// RandomToken generates a random token with the specified key size. func RandomToken(size int) ([]byte, error) { config := &packet.Config{DefaultCipher: packet.CipherAES256} symKey := make([]byte, size) @@ -55,7 +55,8 @@ func RandomToken(size int) ([]byte, error) { return symKey, nil } -// GenerateSessionKeyAlgo generates a random key of the correct length for the specified algorithm +// GenerateSessionKeyAlgo generates a random key of the correct length for the +// specified algorithm. func GenerateSessionKeyAlgo(algo string) (sk *SessionKey, err error) { cf, ok := symKeyAlgos[algo] if !ok { @@ -73,7 +74,7 @@ func GenerateSessionKeyAlgo(algo string) (sk *SessionKey, err error) { return sk, nil } -// GenerateSessionKey generates a random key for the default cipher +// GenerateSessionKey generates a random key for the default cipher. func GenerateSessionKey() (*SessionKey, error) { return GenerateSessionKeyAlgo(constants.AES256) } @@ -105,9 +106,9 @@ func newSessionKeyFromEncrypted(ek *packet.EncryptedKey) (*SessionKey, error) { return symmetricKey, nil } -// Encrypt encrypts a PlainMessage to PGPMessage with a SessionKey -// * message : The plain data as a PlainMessage -// * output : The encrypted data as PGPMessage +// Encrypt encrypts a PlainMessage to PGPMessage with a SessionKey. +// * message : The plain data as a PlainMessage. +// * output : The encrypted data as PGPMessage. func (sk *SessionKey) Encrypt(message *PlainMessage) ([]byte, error) { var encBuf bytes.Buffer var encryptWriter io.WriteCloser @@ -152,9 +153,9 @@ func (sk *SessionKey) Encrypt(message *PlainMessage) ([]byte, error) { return encBuf.Bytes(), nil } -// Decrypt decrypts password protected pgp binary messages -// * encrypted: PGPMessage -// * output: PlainMessage +// Decrypt decrypts password protected pgp binary messages. +// * encrypted: PGPMessage. +// * output: PlainMessage. func (sk *SessionKey) Decrypt(dataPacket []byte) (*PlainMessage, error) { var messageReader = bytes.NewReader(dataPacket) var decrypted io.ReadCloser diff --git a/crypto/signature.go b/crypto/signature.go index 120731d..637386d 100644 --- a/crypto/signature.go +++ b/crypto/signature.go @@ -15,13 +15,14 @@ import ( "github.com/ProtonMail/gopenpgp/v2/internal" ) -// SignatureVerificationError is returned from Decrypt and VerifyDetached functions when signature verification fails +// SignatureVerificationError is returned from Decrypt and VerifyDetached +// functions when signature verification fails. type SignatureVerificationError struct { Status int Message string } -// Error is the base method for all errors +// Error is the base method for all errors. func (e SignatureVerificationError) Error() string { return fmt.Sprintf("Signature Verification Error: %v", e.Message) } @@ -30,7 +31,8 @@ func (e SignatureVerificationError) Error() string { // Internal functions // ------------------ -// newSignatureFailed creates a new SignatureVerificationError, type SIGNATURE_FAILED +// newSignatureFailed creates a new SignatureVerificationError, type +// SignatureFailed. func newSignatureFailed() SignatureVerificationError { return SignatureVerificationError{ constants.SIGNATURE_FAILED, @@ -38,7 +40,8 @@ func newSignatureFailed() SignatureVerificationError { } } -// newSignatureNotSigned creates a new SignatureVerificationError, type SIGNATURE_NOT_SIGNED +// newSignatureNotSigned creates a new SignatureVerificationError, type +// SignatureNotSigned. func newSignatureNotSigned() SignatureVerificationError { return SignatureVerificationError{ constants.SIGNATURE_NOT_SIGNED, @@ -46,7 +49,8 @@ func newSignatureNotSigned() SignatureVerificationError { } } -// newSignatureNoVerifier creates a new SignatureVerificationError, type SIGNATURE_NO_VERIFIER +// newSignatureNoVerifier creates a new SignatureVerificationError, type +// SignatureNoVerifier. func newSignatureNoVerifier() SignatureVerificationError { return SignatureVerificationError{ constants.SIGNATURE_NO_VERIFIER, @@ -54,50 +58,46 @@ func newSignatureNoVerifier() SignatureVerificationError { } } -// processSignatureExpiration handles signature time verification manually, so we can add a margin to the -// creationTime check. +// 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 verifyTime > 0 { - created := md.Signature.CreationTime.Unix() - expires := int64(math.MaxInt64) - if md.Signature.SigLifetimeSecs != nil { - expires = int64(*md.Signature.SigLifetimeSecs) + created - } - if created-internal.CreationTimeOffset <= verifyTime && verifyTime <= expires { - md.SignatureError = nil - } - } else { - // verifyTime = 0: time check disabled, everything is okay - md.SignatureError = nil - } + if md.SignatureError != pgpErrors.ErrSignatureExpired { + return + } + if verifyTime == 0 { + // verifyTime = 0: time check disabled, everything is okay + md.SignatureError = nil + return + } + created := md.Signature.CreationTime.Unix() + expires := int64(math.MaxInt64) + if md.Signature.SigLifetimeSecs != nil { + expires = int64(*md.Signature.SigLifetimeSecs) + created + } + if created-internal.CreationTimeOffset <= verifyTime && verifyTime <= expires { + md.SignatureError = nil } } -// verifyDetailsSignature verifies signature from message details +// verifyDetailsSignature verifies signature from message details. func verifyDetailsSignature(md *openpgp.MessageDetails, verifierKey *KeyRing) error { if md.IsSigned { - if md.SignedBy != nil { - if len(verifierKey.entities) > 0 { - matches := verifierKey.entities.KeysById(md.SignedByKeyId) - if len(matches) > 0 { - if md.SignatureError == nil { - return nil - } - return newSignatureFailed() - } - } else { - return newSignatureNoVerifier() - } - } else { + if md.SignedBy == nil || len(verifierKey.entities) == 0 { return newSignatureNoVerifier() } + matches := verifierKey.entities.KeysById(md.SignedByKeyId) + if len(matches) > 0 { + if md.SignatureError == nil { + return nil + } + return newSignatureFailed() + } } return newSignatureNoVerifier() } -// verifySignature verifies if a signature is valid with the entity list +// verifySignature verifies if a signature is valid with the entity list. func verifySignature(pubKeyEntries openpgp.EntityList, origText io.Reader, signature []byte, verifyTime int64) error { config := &packet.Config{} if verifyTime == 0 { @@ -113,23 +113,22 @@ func verifySignature(pubKeyEntries openpgp.EntityList, origText io.Reader, signa signer, err := openpgp.CheckDetachedSignature(pubKeyEntries, origText, signatureReader, config) - if err == pgpErrors.ErrSignatureExpired && signer != nil { - if 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 - config.Time = func() time.Time { - return time.Unix(verifyTime, 0) - } + if 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 + config.Time = func() time.Time { + return time.Unix(verifyTime, 0) + } - _, err = signatureReader.Seek(0, io.SeekStart) - if err != nil { - return newSignatureFailed() - } + _, err = signatureReader.Seek(0, io.SeekStart) + if err != nil { + return newSignatureFailed() + } - signer, err = openpgp.CheckDetachedSignature(pubKeyEntries, origText, signatureReader, config) - if err != nil { - return newSignatureFailed() - } + signer, err = openpgp.CheckDetachedSignature(pubKeyEntries, origText, signatureReader, config) + if err != nil { + return newSignatureFailed() } } diff --git a/crypto/signature_collector.go b/crypto/signature_collector.go index 47459ff..8b636c0 100644 --- a/crypto/signature_collector.go +++ b/crypto/signature_collector.go @@ -13,7 +13,7 @@ import ( "golang.org/x/crypto/openpgp/packet" ) -// SignatureCollector structure +// SignatureCollector structure. type SignatureCollector struct { config *packet.Config keyring openpgp.KeyRing @@ -32,91 +32,85 @@ func newSignatureCollector( } } -// Accept collects the signature +// Accept collects the signature. func (sc *SignatureCollector) Accept( part io.Reader, header textproto.MIMEHeader, hasPlainSibling, isFirst, isLast bool, ) (err error) { parentMediaType, params, _ := mime.ParseMediaType(header.Get("Content-Type")) - if parentMediaType == "multipart/signed" { - newPart, rawBody := gomime.GetRawMimePart(part, "--"+params["boundary"]) - var multiparts []io.Reader - var multipartHeaders []textproto.MIMEHeader - if multiparts, multipartHeaders, err = gomime.GetMultipartParts(newPart, params); err == nil { - 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 = newSignatureNotSigned() - // Invalid multipart/signed format just pass along - _, err = ioutil.ReadAll(rawBody) - if err != nil { - return err - } - for i, p := range multiparts { - if err = sc.target.Accept(p, multipartHeaders[i], hasPlainChild, true, true); err != nil { - return - } - } + if parentMediaType != "multipart/signed" { + return sc.target.Accept(part, header, hasPlainSibling, isFirst, isLast) + } + + newPart, rawBody := gomime.GetRawMimePart(part, "--"+params["boundary"]) + multiparts, multipartHeaders, err := gomime.GetMultipartParts(newPart, params) + if err != nil { + return + } + + hasPlainChild := false + for _, header := range multipartHeaders { + mediaType, _, _ := mime.ParseMediaType(header.Get("Content-Type")) + hasPlainChild = (mediaType == "text/plain") + } + if len(multiparts) != 2 { + sc.verified = newSignatureNotSigned() + // Invalid multipart/signed format just pass along + if _, err = ioutil.ReadAll(rawBody); err != nil { + return err + } + + for i, p := range multiparts { + if err = sc.target.Accept(p, multipartHeaders[i], hasPlainChild, true, true); err != nil { return } - - // actual multipart/signed format - err = sc.target.Accept(multiparts[0], multipartHeaders[0], hasPlainChild, true, true) - if err != nil { - return err - } - - partData, err := ioutil.ReadAll(multiparts[1]) - if err != nil { - return err - } - - decodedPart := gomime.DecodeContentEncoding( - bytes.NewReader(partData), - multipartHeaders[1].Get("Content-Transfer-Encoding")) - - buffer, err := ioutil.ReadAll(decodedPart) - if err != nil { - return err - } - mediaType, _, _ := mime.ParseMediaType(header.Get("Content-Type")) - buffer, err = gomime.DecodeCharset(buffer, mediaType, params) - if err != nil { - return err - } - sc.signature = string(buffer) - str, _ := ioutil.ReadAll(rawBody) - rawBody = bytes.NewReader(str) - if sc.keyring != nil { - _, err = openpgp.CheckArmoredDetachedSignature(sc.keyring, rawBody, bytes.NewReader(buffer), sc.config) - - if err != nil { - sc.verified = newSignatureFailed() - } else { - sc.verified = nil - } - } else { - sc.verified = newSignatureNoVerifier() - } - return nil } return } - err = sc.target.Accept(part, header, hasPlainSibling, isFirst, isLast) + + // actual multipart/signed format + err = sc.target.Accept(multiparts[0], multipartHeaders[0], hasPlainChild, true, true) if err != nil { return err } - return nil + partData, err := ioutil.ReadAll(multiparts[1]) + if err != nil { + return err + } + + decodedPart := gomime.DecodeContentEncoding( + bytes.NewReader(partData), + multipartHeaders[1].Get("Content-Transfer-Encoding")) + + buffer, err := ioutil.ReadAll(decodedPart) + if err != nil { + return err + } + mediaType, _, _ := mime.ParseMediaType(header.Get("Content-Type")) + buffer, err = gomime.DecodeCharset(buffer, mediaType, params) + if err != nil { + return err + } + sc.signature = string(buffer) + str, _ := ioutil.ReadAll(rawBody) + rawBody = bytes.NewReader(str) + if sc.keyring != nil { + _, err = openpgp.CheckArmoredDetachedSignature(sc.keyring, rawBody, bytes.NewReader(buffer), sc.config) + + if err != nil { + sc.verified = newSignatureFailed() + } else { + sc.verified = nil + } + } else { + sc.verified = newSignatureNoVerifier() + } + return err } -// GetSignature collected by Accept +// GetSignature collected by Accept. func (sc SignatureCollector) GetSignature() string { return sc.signature } diff --git a/crypto/time.go b/crypto/time.go index 06aa3d2..eeb91c9 100644 --- a/crypto/time.go +++ b/crypto/time.go @@ -5,25 +5,25 @@ import ( "time" ) -// UpdateTime updates cached time +// UpdateTime updates cached time. func UpdateTime(newTime int64) { pgp.latestServerTime = newTime pgp.latestClientTime = time.Now() } -// GetUnixTime gets latest cached time +// GetUnixTime gets latest cached time. func GetUnixTime() int64 { return getNow().Unix() } -// GetTime gets latest cached time +// GetTime gets latest cached time. func GetTime() time.Time { return getNow() } // ----- INTERNAL FUNCTIONS ----- -// getNow returns current time +// getNow returns current time. func getNow() time.Time { extrapolate, err := getDiff() @@ -43,7 +43,7 @@ func getDiff() (int64, error) { return 0, errors.New("gopenpgp: latest server time not available") } -// getTimeGenerator Returns a time generator function +// getTimeGenerator Returns a time generator function. func getTimeGenerator() func() time.Time { return getNow } diff --git a/helper/cleartext.go b/helper/cleartext.go index a8c07e5..c48c6ca 100644 --- a/helper/cleartext.go +++ b/helper/cleartext.go @@ -7,8 +7,9 @@ import ( "github.com/ProtonMail/gopenpgp/v2/internal" ) -// SignCleartextMessageArmored signs text given a private key and its passphrase, canonicalizes and trims the newlines, -// and returns the PGP-compliant special armoring +// SignCleartextMessageArmored signs text given a private key and its +// passphrase, canonicalizes and trims the newlines, and returns the +// PGP-compliant special armoring. func SignCleartextMessageArmored(privateKey string, passphrase []byte, text string) (string, error) { signingKey, err := crypto.NewKeyFromArmored(privateKey) if err != nil { @@ -29,8 +30,9 @@ func SignCleartextMessageArmored(privateKey string, passphrase []byte, text stri return SignCleartextMessage(keyRing, text) } -// VerifyCleartextMessageArmored verifies PGP-compliant armored signed plain text given the public key -// and returns the text or err if the verification fails +// VerifyCleartextMessageArmored verifies PGP-compliant armored signed plain +// text given the public key and returns the text or err if the verification +// fails. func VerifyCleartextMessageArmored(publicKey, armored string, verifyTime int64) (string, error) { signingKey, err := crypto.NewKeyFromArmored(publicKey) if err != nil { @@ -45,8 +47,8 @@ func VerifyCleartextMessageArmored(publicKey, armored string, verifyTime int64) return VerifyCleartextMessage(verifyKeyRing, armored, verifyTime) } -// SignCleartextMessage signs text given a private keyring, canonicalizes and trims the newlines, -// and returns the PGP-compliant special armoring +// SignCleartextMessage signs text given a private keyring, canonicalizes and +// trims the newlines, and returns the PGP-compliant special armoring. func SignCleartextMessage(keyRing *crypto.KeyRing, text string) (string, error) { text = canonicalizeAndTrim(text) message := crypto.NewPlainMessageFromString(text) @@ -59,8 +61,9 @@ func SignCleartextMessage(keyRing *crypto.KeyRing, text string) (string, error) return crypto.NewClearTextMessage(message.GetBinary(), signature.GetBinary()).GetArmored() } -// VerifyCleartextMessage verifies PGP-compliant armored signed plain text given the public keyring -// and returns the text or err if the verification fails +// VerifyCleartextMessage verifies PGP-compliant armored signed plain text +// given the public keyring and returns the text or err if the verification +// fails. func VerifyCleartextMessage(keyRing *crypto.KeyRing, armored string, verifyTime int64) (string, error) { clearTextMessage, err := crypto.NewClearTextMessageFromArmored(armored) if err != nil { @@ -79,7 +82,7 @@ func VerifyCleartextMessage(keyRing *crypto.KeyRing, armored string, verifyTime // ----- INTERNAL FUNCTIONS ----- -// canonicalizeAndTrim alters a string canonicalizing and trimming the newlines +// canonicalizeAndTrim alters a string canonicalizing and trimming the newlines. func canonicalizeAndTrim(text string) string { text = internal.TrimNewlines(text) text = strings.Replace(strings.Replace(text, "\r\n", "\n", -1), "\n", "\r\n", -1) diff --git a/helper/helper.go b/helper/helper.go index 892f975..9cf6a8c 100644 --- a/helper/helper.go +++ b/helper/helper.go @@ -1,4 +1,4 @@ -// helper contains several functions with a simple interface to extend usability and compatibility with gomobile +// Package helper contains several functions with a simple interface to extend usability and compatibility with gomobile package helper import ( @@ -7,7 +7,7 @@ import ( "github.com/ProtonMail/gopenpgp/v2/crypto" ) -// EncryptMessageWithPassword encrypts a string with a passphrase using AES256 +// EncryptMessageWithPassword encrypts a string with a passphrase using AES256. func EncryptMessageWithPassword(password []byte, plaintext string) (ciphertext string, err error) { var pgpMessage *crypto.PGPMessage @@ -41,7 +41,8 @@ func DecryptMessageWithPassword(password []byte, ciphertext string) (plaintext s return message.GetString(), nil } -// EncryptMessageArmored generates an armored PGP message given a plaintext and an armored public key +// EncryptMessageArmored generates an armored PGP message given a plaintext and +// an armored public key. func EncryptMessageArmored(key, plaintext string) (ciphertext string, err error) { var publicKey *crypto.Key var publicKeyRing *crypto.KeyRing @@ -68,8 +69,8 @@ func EncryptMessageArmored(key, plaintext string) (ciphertext string, err error) return ciphertext, nil } -// EncryptSignMessageArmored generates an armored signed PGP message given a plaintext and an armored public key -// a private key and its passphrase +// EncryptSignMessageArmored generates an armored signed PGP message given a +// plaintext and an armored public key a private key and its passphrase. func EncryptSignMessageArmored( publicKey, privateKey string, passphrase []byte, plaintext string, ) (ciphertext string, err error) { @@ -111,7 +112,8 @@ func EncryptSignMessageArmored( return ciphertext, nil } -// DecryptMessageArmored decrypts an armored PGP message given a private key and its passphrase +// DecryptMessageArmored decrypts an armored PGP message given a private key +// and its passphrase. func DecryptMessageArmored( privateKey string, passphrase []byte, ciphertext string, ) (plaintext string, err error) { @@ -144,9 +146,9 @@ func DecryptMessageArmored( return message.GetString(), nil } -// DecryptVerifyMessageArmored decrypts an armored PGP message given a private key and its passphrase -// and verifies the embedded signature. -// Returns the plain data or an error on signature verification failure. +// DecryptVerifyMessageArmored decrypts an armored PGP message given a private +// key and its passphrase and verifies the embedded signature. Returns the +// plain data or an error on signature verification failure. func DecryptVerifyMessageArmored( publicKey, privateKey string, passphrase []byte, ciphertext string, ) (plaintext string, err error) { @@ -187,9 +189,10 @@ func DecryptVerifyMessageArmored( return message.GetString(), nil } -// DecryptVerifyAttachment decrypts and verifies an attachment split into the keyPacket, dataPacket -// and an armored (!) signature, given a publicKey, and a privateKey with its passphrase. -// Returns the plain data or an error on signature verification failure. +// DecryptVerifyAttachment decrypts and verifies an attachment split into the +// keyPacket, dataPacket and an armored (!) signature, given a publicKey, and a +// privateKey with its passphrase. Returns the plain data or an error on +// signature verification failure. func DecryptVerifyAttachment( publicKey, privateKey string, passphrase, keyPacket, dataPacket []byte, diff --git a/helper/mobile.go b/helper/mobile.go index 4ad95fd..93ecbeb 100644 --- a/helper/mobile.go +++ b/helper/mobile.go @@ -11,9 +11,9 @@ type ExplicitVerifyMessage struct { SignatureVerificationError *crypto.SignatureVerificationError } -// DecryptVerifyMessageArmored decrypts an armored PGP message given a private key and its passphrase -// and verifies the embedded signature. -// Returns the plain data or an error on signature verification failure. +// DecryptExplicitVerify decrypts an armored PGP message given a private key +// and its passphrase and verifies the embedded signature. Returns the plain +// data or an error on signature verification failure. func DecryptExplicitVerify( pgpMessage *crypto.PGPMessage, privateKeyRing, publicKeyRing *crypto.KeyRing, @@ -57,8 +57,9 @@ func DecryptAttachment(keyPacket []byte, dataPacket []byte, keyRing *crypto.KeyR } // EncryptAttachment encrypts a file given a plainData and a fileName. -// Returns a PGPSplitMessage containing a session key packet and symmetrically encrypted data. -// Specifically designed for attachments rather than text messages. +// Returns a PGPSplitMessage containing a session key packet and symmetrically +// encrypted data. Specifically designed for attachments rather than text +// messages. func EncryptAttachment(plainData []byte, fileName string, keyRing *crypto.KeyRing) (*crypto.PGPSplitMessage, error) { plainMessage := crypto.NewPlainMessage(plainData) decrypted, err := keyRing.EncryptAttachment(plainMessage, fileName) @@ -68,8 +69,8 @@ func EncryptAttachment(plainData []byte, fileName string, keyRing *crypto.KeyRin return decrypted, nil } -// GetJsonSHA256Fingerprints returns the SHA256 fingeprints of key and subkeys, encoded in JSON, since gomobile can not -// handle arrays +// GetJsonSHA256Fingerprints returns the SHA256 fingeprints of key and subkeys, +// encoded in JSON, since gomobile can not handle arrays. func GetJsonSHA256Fingerprints(publicKey string) ([]byte, error) { key, err := crypto.NewKeyFromArmored(publicKey) if err != nil {