go vet and lint
* Naming
* If this is not some OpenPGP standard I follow rule that `DES` should be
upper case as it is abreviation and `Triple` should be camel-case as it
is normal word hence `TripleDES`
* rename `errors2` -> `errorsPGP`
* long lines
* https://github.com/golang/go/wiki/CodeReviewComments#line-length
* I bit improved long lines based on my folding
* reuse type in definition if possible i.e. `a string, b string, c string` -> `a,b,c string`
* `if long_statetent(); err!=nil {` -> `long_statement;↵ if err!=nil {`
* spaces around operators (e.g. `a + b` -> `a+b`)
* removing empty lines on start and end of scope
* comments
* on all exported functions
* start with function name
* import:
* order in alphabet
* separate native, golang.org/x/ and our libs
This commit is contained in:
parent
e03fe86077
commit
78e3abb0d8
16 changed files with 302 additions and 164 deletions
|
|
@ -22,6 +22,7 @@ test-all:
|
|||
- go test -coverprofile cover.out ./...
|
||||
- mkdir reports
|
||||
- go tool cover -html=cover.out -o reports/cover.html
|
||||
- go run vendor/github.com/golangci/golangci-lint/cmd/golangci-lint/main.go run crypto/... --enable-all -D govet -D interfacer -D gofmt -D prealloc -D gochecknoglobals -D goimports -D gosec -D gocritic -D gochecknoinits
|
||||
tags:
|
||||
- coverage
|
||||
artifacts:
|
||||
|
|
|
|||
11
constants/cipher.go
Normal file
11
constants/cipher.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package constants
|
||||
|
||||
// Definitions for cipher suites
|
||||
const (
|
||||
ThreeDES = "3des"
|
||||
TripleDES = "tripledes"
|
||||
CAST5 = "cast5"
|
||||
AES128 = "aes128"
|
||||
AES192 = "aes192"
|
||||
AES256 = "aes256"
|
||||
)
|
||||
|
|
@ -9,12 +9,13 @@ import (
|
|||
"sync"
|
||||
|
||||
armorUtils "github.com/ProtonMail/go-pm-crypto/armor"
|
||||
"github.com/ProtonMail/go-pm-crypto/constants"
|
||||
"github.com/ProtonMail/go-pm-crypto/models"
|
||||
"golang.org/x/crypto/openpgp"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
// EncryptedSplit when encrypt attachment
|
||||
// AttachmentProcessor to encrypt an attachment
|
||||
type AttachmentProcessor struct {
|
||||
w *io.WriteCloser
|
||||
pipe *io.PipeWriter
|
||||
|
|
@ -26,7 +27,9 @@ type AttachmentProcessor struct {
|
|||
|
||||
// Process allows the attachment processor to write the encrypted attachment
|
||||
func (ap *AttachmentProcessor) Process(plainData []byte) {
|
||||
(*ap.w).Write(plainData)
|
||||
if _, err := (*ap.w).Write(plainData); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Finish attachment process
|
||||
|
|
@ -43,11 +46,12 @@ func (ap *AttachmentProcessor) Finish() (*models.EncryptedSplit, error) {
|
|||
return ap.split, nil
|
||||
}
|
||||
|
||||
// Encrypts attachment. Takes input data and key data in binary form
|
||||
func (pm *PmCrypto) encryptAttachment(estimatedSize int, fileName string, publicKey *KeyRing, garbageCollector int) (*AttachmentProcessor, error) {
|
||||
// encryptAttachment takes input data from file
|
||||
func (pm *PmCrypto) encryptAttachment(
|
||||
estimatedSize int, fileName string, publicKey *KeyRing, garbageCollector int,
|
||||
) (*AttachmentProcessor, error) {
|
||||
attachmentProc := &AttachmentProcessor{}
|
||||
// you can also add these one at
|
||||
// a time if you need to
|
||||
// you can also add these one at a time if you need to
|
||||
attachmentProc.done.Add(1)
|
||||
attachmentProc.garbageCollector = garbageCollector
|
||||
|
||||
|
|
@ -68,7 +72,7 @@ func (pm *PmCrypto) encryptAttachment(estimatedSize int, fileName string, public
|
|||
if attachmentProc.err != nil {
|
||||
attachmentProc.err = splitError
|
||||
}
|
||||
split.Algo = "aes256"
|
||||
split.Algo = constants.AES256
|
||||
attachmentProc.split = split
|
||||
}()
|
||||
|
||||
|
|
@ -84,8 +88,11 @@ func (pm *PmCrypto) encryptAttachment(estimatedSize int, fileName string, public
|
|||
return attachmentProc, nil
|
||||
}
|
||||
|
||||
// EncryptAttachment encrypts attachment. Takes input data and key data in binary form
|
||||
func (pm *PmCrypto) EncryptAttachment(plainData []byte, fileName string, publicKey *KeyRing) (*models.EncryptedSplit, error) {
|
||||
// EncryptAttachment encrypts attachment. Takes input data and key data in
|
||||
// binary form
|
||||
func (pm *PmCrypto) EncryptAttachment(
|
||||
plainData []byte, fileName string, publicKey *KeyRing,
|
||||
) (*models.EncryptedSplit, error) {
|
||||
ap, err := pm.encryptAttachment(len(plainData), fileName, publicKey, -1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -96,12 +103,12 @@ func (pm *PmCrypto) EncryptAttachment(plainData []byte, fileName string, publicK
|
|||
return nil, err
|
||||
}
|
||||
return split, nil
|
||||
|
||||
}
|
||||
|
||||
// EncryptAttachmentLowMemory ...
|
||||
func (pm *PmCrypto) EncryptAttachmentLowMemory(estimatedSize int, fileName string, publicKey *KeyRing) (*AttachmentProcessor, error) {
|
||||
// Garbage collect every megabyte
|
||||
// EncryptAttachmentLowMemory with garbage collected every megabyte
|
||||
func (pm *PmCrypto) EncryptAttachmentLowMemory(
|
||||
estimatedSize int, fileName string, publicKey *KeyRing,
|
||||
) (*AttachmentProcessor, error) {
|
||||
return pm.encryptAttachment(estimatedSize, fileName, publicKey, 1<<20)
|
||||
}
|
||||
|
||||
|
|
@ -119,9 +126,12 @@ func SplitArmor(encrypted string) (*models.EncryptedSplit, error) {
|
|||
return SeparateKeyAndData(nil, encryptedReader, len(encrypted), -1)
|
||||
}
|
||||
|
||||
// Decrypt attachment. Takes input data and key data in binary form. privateKeys can contains more keys. passphrase is used to unlock keys
|
||||
func (pm *PmCrypto) DecryptAttachment(keyPacket []byte, dataPacket []byte, kr *KeyRing, passphrase string) ([]byte, error) {
|
||||
|
||||
// DecryptAttachment takes input data and key data in binary form. The
|
||||
// privateKeys can contains more keys. The passphrase is used to unlock keys
|
||||
func (pm *PmCrypto) DecryptAttachment(
|
||||
keyPacket, dataPacket []byte,
|
||||
kr *KeyRing, passphrase string,
|
||||
) ([]byte, error) {
|
||||
privKeyEntries := kr.entities
|
||||
|
||||
if err := kr.Unlock([]byte(passphrase)); err != nil {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"encoding/base64"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// const testAttachmentEncrypted = `0ksB0fHC6Duezx/0TqpK/82HSl8+qCY0c2BCuyrSFoj6Dubd93T3//32jVYa624NYvfvxX+UxFKYKJxG09gFsU1IVc87cWvUgmUmgjU=`
|
||||
// const testAttachmentEncrypted =
|
||||
// `0ksB0fHC6Duezx/0TqpK/82HSl8+qCY0c2BCuyrSFoj6Dubd93T3//32jVYa624NYvfvxX+UxFKYKJxG09gFsU1IVc87cWvUgmUmgjU=`
|
||||
|
||||
func TestAttachmentGetKey(t *testing.T) {
|
||||
testKeyPackets, err := ioutil.ReadFile("testdata/attachment_keypacket")
|
||||
|
|
@ -22,7 +24,11 @@ func TestAttachmentGetKey(t *testing.T) {
|
|||
t.Fatal("Expected no error while decoding base64 KeyPacket, got:", err)
|
||||
}
|
||||
|
||||
split, err := SeparateKeyAndData(testPrivateKeyRing, strings.NewReader(string(testKeyPacketsDecoded)), len(testKeyPacketsDecoded), -1)
|
||||
split, err := SeparateKeyAndData(
|
||||
testPrivateKeyRing,
|
||||
strings.NewReader(string(testKeyPacketsDecoded)),
|
||||
len(testKeyPacketsDecoded),
|
||||
-1)
|
||||
if err != nil {
|
||||
t.Fatal("Expected no error while decrypting attachment key, got:", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,15 +33,16 @@ type SymmetricKey struct {
|
|||
const SymmetricallyEncryptedTag = 210
|
||||
|
||||
var symKeyAlgos = map[string]packet.CipherFunction{
|
||||
"3des": packet.Cipher3DES,
|
||||
"tripledes": packet.Cipher3DES,
|
||||
"cast5": packet.CipherCAST5,
|
||||
"aes128": packet.CipherAES128,
|
||||
"aes192": packet.CipherAES192,
|
||||
"aes256": packet.CipherAES256,
|
||||
constants.ThreeDES: packet.Cipher3DES,
|
||||
constants.TripleDES: packet.Cipher3DES,
|
||||
constants.CAST5: packet.CipherCAST5,
|
||||
constants.AES128: packet.CipherAES128,
|
||||
constants.AES192: packet.CipherAES192,
|
||||
constants.AES256: packet.CipherAES256,
|
||||
}
|
||||
|
||||
// GetCipherFunc returns function corresponding to an algorithm used in this SymmetricKey
|
||||
// GetCipherFunc returns function corresponding to an algorithm used in
|
||||
// this SymmetricKey
|
||||
func (sk *SymmetricKey) GetCipherFunc() packet.CipherFunction {
|
||||
cf, ok := symKeyAlgos[sk.Algo]
|
||||
if ok {
|
||||
|
|
@ -108,7 +109,10 @@ func DecryptAttKey(kr *KeyRing, keyPacket string) (key *SymmetricKey, err error)
|
|||
}
|
||||
|
||||
// SeparateKeyAndData from packets in a pgp session
|
||||
func SeparateKeyAndData(kr *KeyRing, r io.Reader, estimatedLength int, garbageCollector int) (outSplit *models.EncryptedSplit, err error) {
|
||||
func SeparateKeyAndData(
|
||||
kr *KeyRing, r io.Reader,
|
||||
estimatedLength, garbageCollector int,
|
||||
) (outSplit *models.EncryptedSplit, err error) {
|
||||
// For info on each, see: https://golang.org/pkg/runtime/#MemStats
|
||||
packets := packet.NewReader(r)
|
||||
outSplit = &models.EncryptedSplit{}
|
||||
|
|
@ -254,21 +258,20 @@ func SetKey(kr *KeyRing, symKey *SymmetricKey) (packets string, err error) {
|
|||
}
|
||||
if pub == nil {
|
||||
err = fmt.Errorf("pm-crypto: cannot set key: no public key available")
|
||||
return
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err = packet.SerializeEncryptedKey(w, pub, cf, symKey.Key, nil); err != nil {
|
||||
err = fmt.Errorf("pm-crypto: cannot set key: %v", err)
|
||||
return
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err = w.Close(); err != nil {
|
||||
err = fmt.Errorf("pm-crypto: cannot set key: %v", err)
|
||||
return
|
||||
return "", err
|
||||
}
|
||||
|
||||
packets = b.String()
|
||||
return
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
// IsKeyExpiredBin checks if the given key is expired. Input in binary format
|
||||
|
|
@ -339,9 +342,11 @@ func (pm *PmCrypto) IsKeyExpired(publicKey string) (bool, error) {
|
|||
return pm.IsKeyExpiredBin(rawPubKey)
|
||||
}
|
||||
|
||||
func (pm *PmCrypto) generateKey(userName string, domain string, passphrase string, keyType string, bits int,
|
||||
prime1 []byte, prime2 []byte, prime3 []byte, prime4 []byte) (string, error) {
|
||||
|
||||
func (pm *PmCrypto) generateKey(
|
||||
userName, domain, passphrase, keyType string,
|
||||
bits int,
|
||||
prime1, prime2, prime3, prime4 []byte,
|
||||
) (string, error) {
|
||||
if len(userName) <= 0 {
|
||||
return "", errors.New("invalid user name format")
|
||||
}
|
||||
|
|
@ -421,12 +426,15 @@ func (pm *PmCrypto) GenerateRSAKeyWithPrimes(
|
|||
}
|
||||
|
||||
// GenerateKey and generate primes
|
||||
func (pm *PmCrypto) GenerateKey(userName string, domain string, passphrase string, keyType string, bits int) (string, error) {
|
||||
func (pm *PmCrypto) GenerateKey(userName, domain, passphrase, keyType string, bits int) (string, error) {
|
||||
return pm.generateKey(userName, domain, passphrase, keyType, bits, nil, nil, nil, nil)
|
||||
}
|
||||
|
||||
// UpdatePrivateKeyPassphrase decrypts the given private key with oldPhrase and re-encrypts with the newPassphrase
|
||||
func (pm *PmCrypto) UpdatePrivateKeyPassphrase(privateKey string, oldPassphrase string, newPassphrase string) (string, error) {
|
||||
// UpdatePrivateKeyPassphrase decrypts the given private key with oldPhrase and
|
||||
// re-encrypts with the newPassphrase
|
||||
func (pm *PmCrypto) UpdatePrivateKeyPassphrase(
|
||||
privateKey string, oldPassphrase string, newPassphrase string,
|
||||
) (string, error) {
|
||||
privKey := strings.NewReader(privateKey)
|
||||
privKeyEntries, err := openpgp.ReadArmoredKeyRing(privKey)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"encoding/base64"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ProtonMail/go-pm-crypto/constants"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const name = "richard.stallman"
|
||||
|
|
@ -38,7 +40,7 @@ func TestGenerateKeys(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGenerateKeyRings(t *testing.T) {
|
||||
rsaPrivateKeyRing, err = ReadArmoredKeyRing(strings.NewReader(rsaKey));
|
||||
rsaPrivateKeyRing, err = ReadArmoredKeyRing(strings.NewReader(rsaKey))
|
||||
if err != nil {
|
||||
t.Fatal("Cannot read RSA key:", err)
|
||||
}
|
||||
|
|
@ -48,7 +50,7 @@ func TestGenerateKeyRings(t *testing.T) {
|
|||
t.Fatal("Cannot extract RSA public key:", err)
|
||||
}
|
||||
|
||||
rsaPublicKeyRing, err = ReadArmoredKeyRing(strings.NewReader(rsaPublicKey));
|
||||
rsaPublicKeyRing, err = ReadArmoredKeyRing(strings.NewReader(rsaPublicKey))
|
||||
if err != nil {
|
||||
t.Fatal("Cannot read RSA public key:", err)
|
||||
}
|
||||
|
|
@ -58,7 +60,7 @@ func TestGenerateKeyRings(t *testing.T) {
|
|||
t.Fatal("Cannot decrypt RSA key:", err)
|
||||
}
|
||||
|
||||
ecPrivateKeyRing, err = ReadArmoredKeyRing(strings.NewReader(ecKey));
|
||||
ecPrivateKeyRing, err = ReadArmoredKeyRing(strings.NewReader(ecKey))
|
||||
if err != nil {
|
||||
t.Fatal("Cannot read EC key:", err)
|
||||
}
|
||||
|
|
@ -68,7 +70,7 @@ func TestGenerateKeyRings(t *testing.T) {
|
|||
t.Fatal("Cannot extract EC public key:", err)
|
||||
}
|
||||
|
||||
ecPublicKeyRing, err = ReadArmoredKeyRing(strings.NewReader(ecPublicKey));
|
||||
ecPublicKeyRing, err = ReadArmoredKeyRing(strings.NewReader(ecPublicKey))
|
||||
if err != nil {
|
||||
t.Fatal("Cannot read EC public key:", err)
|
||||
}
|
||||
|
|
@ -83,7 +85,7 @@ func TestEncryptDecryptKeys(t *testing.T) {
|
|||
var pass, _ = base64.StdEncoding.DecodeString("H2CAwzpdexjxXucVYMERDiAc/td8aGPrr6ZhfMnZlLI=")
|
||||
var testSymmetricKey = &SymmetricKey{
|
||||
Key: pass,
|
||||
Algo: "aes256",
|
||||
Algo: constants.AES256,
|
||||
}
|
||||
|
||||
packet, err := SetKey(rsaPublicKeyRing, testSymmetricKey)
|
||||
|
|
@ -122,8 +124,9 @@ func TestUpdatePrivateKeysPassphrase(t *testing.T) {
|
|||
passphrase = newPassphrase
|
||||
}
|
||||
|
||||
func ExampleCheckKeys() {
|
||||
pmCrypto.CheckKey(readTestFile("keyring_publicKey"))
|
||||
// ExampleCheckKey to track changes in test data
|
||||
func ExampleCheckKey() {
|
||||
_, _ = pmCrypto.CheckKey(readTestFile("keyring_publicKey"))
|
||||
// Output:
|
||||
// SubKey:37e4bcf09b36e34012d10c0247dc67b5cb8267f6
|
||||
// PrimaryKey:6e8ba229b0cccaf6962f97953eb6259edf21df24
|
||||
|
|
|
|||
|
|
@ -115,22 +115,28 @@ func (kr *KeyRing) GetEntities() openpgp.EntityList {
|
|||
}
|
||||
|
||||
// GetSigningEntity returns first private signing entity from keyring
|
||||
func (kr *KeyRing) GetSigningEntity(passphrase string) *openpgp.Entity {
|
||||
func (kr *KeyRing) GetSigningEntity(passphrase string) (*openpgp.Entity, error) {
|
||||
var signEntity *openpgp.Entity
|
||||
|
||||
for _, e := range kr.entities {
|
||||
// Entity.PrivateKey must be a signing key
|
||||
if e.PrivateKey != nil {
|
||||
if e.PrivateKey.Encrypted {
|
||||
e.PrivateKey.Decrypt([]byte(passphrase))
|
||||
}
|
||||
if !e.PrivateKey.Encrypted {
|
||||
if err := e.PrivateKey.Decrypt([]byte(passphrase)); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
signEntity = e
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return signEntity
|
||||
if signEntity == nil {
|
||||
err := errors.New("pmcrypto: cannot sign message, unable to unlock signer key")
|
||||
return signEntity, err
|
||||
}
|
||||
|
||||
return signEntity, nil
|
||||
}
|
||||
|
||||
// Encrypt encrypts data to this keyring's owner. If sign is not nil, it also
|
||||
|
|
@ -161,11 +167,19 @@ func (kr *KeyRing) Encrypt(w io.Writer, sign *KeyRing, filename string, canonica
|
|||
}
|
||||
}
|
||||
|
||||
return EncryptCore(w, encryptEntities, signEntity, filename, canonicalizeText, func() time.Time { return GetPmCrypto().GetTime() })
|
||||
return EncryptCore(
|
||||
w,
|
||||
encryptEntities,
|
||||
signEntity,
|
||||
filename,
|
||||
canonicalizeText,
|
||||
func() time.Time { return GetPmCrypto().GetTime() })
|
||||
}
|
||||
|
||||
// EncryptCore is common encryption method for desktop and mobile clients
|
||||
func EncryptCore(w io.Writer, encryptEntities []*openpgp.Entity, signEntity *openpgp.Entity, filename string, canonicalizeText bool, timeGenerator func() time.Time) (io.WriteCloser, error) {
|
||||
func EncryptCore(w io.Writer, encryptEntities []*openpgp.Entity, signEntity *openpgp.Entity, filename string,
|
||||
canonicalizeText bool, timeGenerator func() time.Time) (io.WriteCloser, error) {
|
||||
|
||||
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: timeGenerator}
|
||||
|
||||
hints := &openpgp.FileHints{
|
||||
|
|
@ -235,7 +249,9 @@ func (kr *KeyRing) EncryptString(s string, sign *KeyRing) (encrypted string, err
|
|||
}
|
||||
|
||||
// EncryptSymmetric data using generated symmetric key encrypted with this KeyRing
|
||||
func (kr *KeyRing) EncryptSymmetric(textToEncrypt string, canonicalizeText bool) (outSplit *models.EncryptedSplit, err error) {
|
||||
func (kr *KeyRing) EncryptSymmetric(textToEncrypt string, canonicalizeText bool) (outSplit *models.EncryptedSplit,
|
||||
err error) {
|
||||
|
||||
var encryptedWriter io.WriteCloser
|
||||
buffer := &bytes.Buffer{}
|
||||
|
||||
|
|
@ -277,7 +293,9 @@ func (kr *KeyRing) DecryptString(encrypted string) (SignedString, error) {
|
|||
// If error is errors.ErrSignatureExpired (from golang.org/x/crypto/openpgp/errors),
|
||||
// contents are still provided if library clients wish to process this message further
|
||||
func (kr *KeyRing) DecryptStringIfNeeded(data string) (decrypted string, err error) {
|
||||
if re := regexp.MustCompile("^-----BEGIN " + constants.PGPMessageHeader + "-----(?s:.+)-----END " + constants.PGPMessageHeader + "-----"); re.MatchString(data) {
|
||||
if re := regexp.MustCompile("^-----BEGIN " + constants.PGPMessageHeader + "-----(?s:.+)-----END " +
|
||||
constants.PGPMessageHeader + "-----"); re.MatchString(data) {
|
||||
|
||||
var signed SignedString
|
||||
signed, err = kr.DecryptString(data)
|
||||
decrypted = signed.String
|
||||
|
|
@ -328,11 +346,7 @@ func (kr *KeyRing) DetachedSign(w io.Writer, toSign io.Reader, canonicalizeText
|
|||
err = openpgp.DetachSign(w, signEntity, toSign, config)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// VerifyString may return errors.ErrSignatureExpired (defined in
|
||||
|
|
@ -377,7 +391,9 @@ func (kr *KeyRing) Unlock(passphrase []byte) error {
|
|||
|
||||
// Entity.Subkeys can be used for encryption
|
||||
for _, subKey := range e.Subkeys {
|
||||
if subKey.PrivateKey != nil && (!subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications) {
|
||||
if subKey.PrivateKey != nil && (!subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage ||
|
||||
subKey.Sig.FlagEncryptCommunications) {
|
||||
|
||||
keys = append(keys, subKey.PrivateKey)
|
||||
}
|
||||
}
|
||||
|
|
@ -516,10 +532,8 @@ func (kr *KeyRing) CheckPassphrase(passphrase string) bool {
|
|||
n++
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
return n != 0
|
||||
}
|
||||
|
||||
// readFrom reads unarmored and armored keys from r and adds them to the keyring.
|
||||
|
|
@ -536,18 +550,28 @@ func (kr *KeyRing) readFrom(r io.Reader, armored bool) error {
|
|||
switch entity.PrivateKey.PrivateKey.(type) {
|
||||
// TODO: type mismatch after crypto lib update, fix this:
|
||||
case *rsa.PrivateKey:
|
||||
entity.PrimaryKey = packet.NewRSAPublicKey(time.Now(), entity.PrivateKey.PrivateKey.(*rsa.PrivateKey).Public().(*xrsa.PublicKey))
|
||||
entity.PrimaryKey = packet.NewRSAPublicKey(
|
||||
time.Now(),
|
||||
entity.PrivateKey.PrivateKey.(*rsa.PrivateKey).Public().(*xrsa.PublicKey))
|
||||
|
||||
case *ecdsa.PrivateKey:
|
||||
entity.PrimaryKey = packet.NewECDSAPublicKey(time.Now(), entity.PrivateKey.PrivateKey.(*ecdsa.PrivateKey).Public().(*ecdsa.PublicKey))
|
||||
entity.PrimaryKey = packet.NewECDSAPublicKey(
|
||||
time.Now(),
|
||||
entity.PrivateKey.PrivateKey.(*ecdsa.PrivateKey).Public().(*ecdsa.PublicKey))
|
||||
}
|
||||
}
|
||||
for _, subkey := range entity.Subkeys {
|
||||
if subkey.PrivateKey != nil {
|
||||
switch subkey.PrivateKey.PrivateKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
subkey.PublicKey = packet.NewRSAPublicKey(time.Now(), subkey.PrivateKey.PrivateKey.(*rsa.PrivateKey).Public().(*xrsa.PublicKey))
|
||||
subkey.PublicKey = packet.NewRSAPublicKey(
|
||||
time.Now(),
|
||||
subkey.PrivateKey.PrivateKey.(*rsa.PrivateKey).Public().(*xrsa.PublicKey))
|
||||
|
||||
case *ecdsa.PrivateKey:
|
||||
subkey.PublicKey = packet.NewECDSAPublicKey(time.Now(), subkey.PrivateKey.PrivateKey.(*ecdsa.PrivateKey).Public().(*ecdsa.PublicKey))
|
||||
subkey.PublicKey = packet.NewECDSAPublicKey(
|
||||
time.Now(),
|
||||
subkey.PrivateKey.PrivateKey.(*ecdsa.PrivateKey).Public().(*ecdsa.PublicKey))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -607,10 +631,13 @@ func (kr *KeyRing) UnmarshalJSON(b []byte) (err error) {
|
|||
if i == 0 {
|
||||
kr.FirstKeyID = ko.ID
|
||||
}
|
||||
kr.readFrom(ko.PrivateKeyReader(), true)
|
||||
err = kr.readFrom(ko.PrivateKeyReader(), true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
// Identities returns the list of identities associated with this key ring.
|
||||
|
|
@ -656,7 +683,7 @@ func ReadKeyRing(r io.Reader) (kr *KeyRing, err error) {
|
|||
func FilterExpiredKeys(contactKeys []*KeyRing) (filteredKeys []*KeyRing, err error) {
|
||||
now := time.Now()
|
||||
hasExpiredEntity := false
|
||||
filteredKeys = make([]*KeyRing, 0, 0)
|
||||
filteredKeys = make([]*KeyRing, 0)
|
||||
|
||||
for _, contactKeyRing := range contactKeys {
|
||||
keyRingHasUnexpiredEntity := false
|
||||
|
|
@ -688,5 +715,5 @@ func FilterExpiredKeys(contactKeys []*KeyRing) (filteredKeys []*KeyRing, err err
|
|||
return filteredKeys, errors.New("all contacts keys are expired")
|
||||
}
|
||||
|
||||
return
|
||||
return filteredKeys, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,29 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"encoding/base64"
|
||||
"golang.org/x/crypto/openpgp/armor"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/openpgp/armor"
|
||||
|
||||
"github.com/ProtonMail/go-pm-crypto/constants"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var decodedSymmetricKey, _ = base64.StdEncoding.DecodeString("ExXmnSiQ2QCey20YLH6qlLhkY3xnIBC1AwlIXwK/HvY=");
|
||||
var decodedSymmetricKey, _ = base64.StdEncoding.DecodeString("ExXmnSiQ2QCey20YLH6qlLhkY3xnIBC1AwlIXwK/HvY=")
|
||||
|
||||
var testSymmetricKey = &SymmetricKey{
|
||||
Key: decodedSymmetricKey,
|
||||
Algo: "aes256",
|
||||
Algo: constants.AES256,
|
||||
}
|
||||
|
||||
// Corresponding key in testdata/keyring_privateKey
|
||||
const testMailboxPassword = "apple"
|
||||
|
||||
// Corresponding key in testdata/keyring_privateKeyLegacy
|
||||
const testMailboxPasswordLegacy = "123"
|
||||
// const testMailboxPasswordLegacy = "123"
|
||||
|
||||
const testToken = "d79ca194a22810a5363eeddfdef7dfbc327c6229"
|
||||
|
||||
|
|
@ -29,22 +32,25 @@ var (
|
|||
testPublicKeyRing *KeyRing
|
||||
)
|
||||
|
||||
var testIdentity = &Identity{
|
||||
Name: "UserID",
|
||||
Email: "",
|
||||
}
|
||||
// var testIdentity = &Identity{
|
||||
// Name: "UserID",
|
||||
// Email: "",
|
||||
// }
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
if testPrivateKeyRing, err = ReadArmoredKeyRing(strings.NewReader(readTestFile("keyring_privateKey"))); err != nil {
|
||||
testPrivateKeyRing, err = ReadArmoredKeyRing(strings.NewReader(readTestFile("keyring_privateKey")))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if testPublicKeyRing, err = ReadArmoredKeyRing(strings.NewReader(readTestFile("keyring_publicKey"))); err != nil {
|
||||
testPublicKeyRing, err = ReadArmoredKeyRing(strings.NewReader(readTestFile("keyring_publicKey")))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := testPrivateKeyRing.Unlock([]byte(testMailboxPassword)); err != nil {
|
||||
err = testPrivateKeyRing.Unlock([]byte(testMailboxPassword))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package crypto
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
|
@ -20,11 +19,13 @@ import (
|
|||
"github.com/ProtonMail/go-pm-crypto/models"
|
||||
)
|
||||
|
||||
// DecryptMessageStringKey decrypts encrypted message use private key (string )
|
||||
// DecryptMessageStringKey decrypts encrypted message use private key (string)
|
||||
// encryptedText : string armored encrypted
|
||||
// privateKey : armored private use to decrypt message
|
||||
// passphrase : match with private key to decrypt message
|
||||
func (pm *PmCrypto) DecryptMessageStringKey(encryptedText string, privateKey string, passphrase string) (string, error) {
|
||||
func (pm *PmCrypto) DecryptMessageStringKey(
|
||||
encryptedText, privateKey, passphrase string,
|
||||
) (string, error) {
|
||||
privKeyRaw, err := armorUtils.Unarmor(privateKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
@ -58,7 +59,11 @@ func (pm *PmCrypto) DecryptMessage(encryptedText string, privateKey *KeyRing, pa
|
|||
return string(b), nil
|
||||
}
|
||||
|
||||
func decryptCore(encryptedText string, additionalEntries openpgp.EntityList, privKey *KeyRing, passphrase string, timeFunc func() time.Time) (*openpgp.MessageDetails, error) {
|
||||
func decryptCore(
|
||||
encryptedText string, additionalEntries openpgp.EntityList,
|
||||
privKey *KeyRing, passphrase string,
|
||||
timeFunc func() time.Time,
|
||||
) (*openpgp.MessageDetails, error) {
|
||||
rawPwd := []byte(passphrase)
|
||||
if err := privKey.Unlock(rawPwd); err != nil {
|
||||
err = fmt.Errorf("pm-crypto: cannot decrypt passphrase: %v", err)
|
||||
|
|
@ -66,14 +71,9 @@ func decryptCore(encryptedText string, additionalEntries openpgp.EntityList, pri
|
|||
}
|
||||
|
||||
privKeyEntries := privKey.entities
|
||||
for _, entity := range privKey.entities {
|
||||
privKeyEntries = append(privKeyEntries, entity)
|
||||
}
|
||||
|
||||
if additionalEntries != nil {
|
||||
for _, e := range additionalEntries {
|
||||
privKeyEntries = append(privKeyEntries, e)
|
||||
}
|
||||
privKeyEntries = append(privKeyEntries, additionalEntries...)
|
||||
}
|
||||
|
||||
encryptedio, err := internal.Unarmor(encryptedText)
|
||||
|
|
@ -92,7 +92,10 @@ func decryptCore(encryptedText string, additionalEntries openpgp.EntityList, pri
|
|||
// verifierKey []byte: unarmored verifier keys
|
||||
// privateKeyRing []byte: unarmored private key to decrypt. could be multiple
|
||||
// passphrase: match with private key to decrypt message
|
||||
func (pm *PmCrypto) DecryptMessageVerify(encryptedText string, verifierKey *KeyRing, privateKeyRing *KeyRing, passphrase string, verifyTime int64) (*models.DecryptSignedVerify, error) {
|
||||
func (pm *PmCrypto) DecryptMessageVerify(
|
||||
encryptedText string, verifierKey, privateKeyRing *KeyRing,
|
||||
passphrase string, verifyTime int64,
|
||||
) (*models.DecryptSignedVerify, error) {
|
||||
out := &models.DecryptSignedVerify{}
|
||||
out.Verify = failed
|
||||
|
||||
|
|
@ -101,7 +104,16 @@ func (pm *PmCrypto) DecryptMessageVerify(encryptedText string, verifierKey *KeyR
|
|||
out.Verify = noVerifier
|
||||
}
|
||||
|
||||
md, err := decryptCore(encryptedText, verifierEntries, privateKeyRing, passphrase, func() time.Time { return time.Unix(0, 0) }) // TODO: I doubt this time is correct
|
||||
md, err := decryptCore(
|
||||
encryptedText,
|
||||
verifierEntries,
|
||||
privateKeyRing,
|
||||
passphrase,
|
||||
func() time.Time { return time.Unix(0, 0) }) // TODO: I doubt this time is correct
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decrypted := md.UnverifiedBody
|
||||
b, err := ioutil.ReadAll(decrypted)
|
||||
|
|
@ -136,7 +148,8 @@ func (pm *PmCrypto) DecryptMessageVerify(encryptedText string, verifierKey *KeyR
|
|||
return out, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
@ -159,7 +172,6 @@ func processSignatureExpiration(md *openpgp.MessageDetails, verifyTime int64) {
|
|||
// plainText string: clear text
|
||||
// output string: armored pgp message
|
||||
func (pm *PmCrypto) EncryptMessageWithPassword(plainText string, password string) (string, error) {
|
||||
|
||||
var outBuf bytes.Buffer
|
||||
w, err := armor.Encode(&outBuf, constants.PGPMessageHeader, internal.ArmorHeaders)
|
||||
if err != nil {
|
||||
|
|
@ -185,14 +197,17 @@ func (pm *PmCrypto) EncryptMessageWithPassword(plainText string, password string
|
|||
return outBuf.String(), nil
|
||||
}
|
||||
|
||||
// EncryptMessage encrypts message with unarmored public key, if pass private key and passphrase will also sign the message
|
||||
// EncryptMessage encrypts message with unarmored public key, if pass private key and passphrase will also sign
|
||||
// the message
|
||||
// publicKey : bytes unarmored public key
|
||||
// plainText : the input
|
||||
// privateKey : optional required when you want to sign
|
||||
// passphrase : optional required when you pass the private key and this passphrase should decrypt the private key
|
||||
// trim : bool true if need to trim new lines
|
||||
func (pm *PmCrypto) EncryptMessage(plainText string, publicKey *KeyRing, privateKey *KeyRing, passphrase string, trim bool) (string, error) {
|
||||
|
||||
func (pm *PmCrypto) EncryptMessage(
|
||||
plainText string, publicKey, privateKey *KeyRing,
|
||||
passphrase string, trim bool,
|
||||
) (string, error) {
|
||||
if trim {
|
||||
plainText = internal.TrimNewlines(plainText)
|
||||
}
|
||||
|
|
@ -205,20 +220,22 @@ func (pm *PmCrypto) EncryptMessage(plainText string, publicKey *KeyRing, private
|
|||
var signEntity *openpgp.Entity
|
||||
|
||||
if len(passphrase) > 0 && len(privateKey.entities) > 0 {
|
||||
|
||||
signEntity := privateKey.GetSigningEntity(passphrase)
|
||||
|
||||
if signEntity == nil {
|
||||
return "", errors.New("cannot sign message, signer key is not unlocked")
|
||||
var err error
|
||||
signEntity, err = privateKey.GetSigningEntity(passphrase)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
ew, err := EncryptCore(w, publicKey.entities, signEntity, "", false, pm.getTimeGenerator())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, _ = ew.Write([]byte(plainText))
|
||||
_, err = ew.Write([]byte(plainText))
|
||||
ew.Close()
|
||||
w.Close()
|
||||
return outBuf.String(), nil
|
||||
return outBuf.String(), err
|
||||
}
|
||||
|
||||
// DecryptMessageWithPassword decrypts a pgp message with a password
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ import (
|
|||
"golang.org/x/crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
func (pm PmCrypto) parseMIME(mimeBody string, verifierKey *KeyRing) (*pmmime.BodyCollector, int, []string, []string, error) {
|
||||
func (pm PmCrypto) parseMIME(
|
||||
mimeBody string, verifierKey *KeyRing,
|
||||
) (*pmmime.BodyCollector, int, []string, []string, error) {
|
||||
mm, err := mail.ReadMessage(strings.NewReader(mimeBody))
|
||||
if err != nil {
|
||||
return nil, 0, nil, nil, err
|
||||
|
|
@ -22,6 +24,9 @@ func (pm PmCrypto) parseMIME(mimeBody string, verifierKey *KeyRing) (*pmmime.Bod
|
|||
|
||||
h := textproto.MIMEHeader(mm.Header)
|
||||
mmBodyData, err := ioutil.ReadAll(mm.Body)
|
||||
if err != nil {
|
||||
return nil, 0, nil, nil, err
|
||||
}
|
||||
|
||||
printAccepter := pmmime.NewMIMEPrinter()
|
||||
bodyCollector := pmmime.NewBodyCollector(printAccepter)
|
||||
|
|
@ -42,10 +47,10 @@ func (pm PmCrypto) parseMIME(mimeBody string, verifierKey *KeyRing) (*pmmime.Bod
|
|||
atts := attachmentsCollector.GetAttachments()
|
||||
attHeaders := attachmentsCollector.GetAttHeaders()
|
||||
|
||||
return body, verified, atts, attHeaders, nil
|
||||
return body, verified, atts, attHeaders, err
|
||||
}
|
||||
|
||||
// MIMECallbacks defines a call back interface
|
||||
// MIMECallbacks defines a call back methods to process MIME message
|
||||
type MIMECallbacks interface {
|
||||
OnBody(body string, mimetype string)
|
||||
OnAttachment(headers string, data []byte)
|
||||
|
|
@ -56,8 +61,10 @@ type MIMECallbacks interface {
|
|||
}
|
||||
|
||||
// DecryptMIMEMessage decrypts a MIME message
|
||||
func (pm *PmCrypto) DecryptMIMEMessage(encryptedText string, verifierKey *KeyRing, privateKeyRing *KeyRing,
|
||||
passphrase string, callbacks MIMECallbacks, verifyTime int64) {
|
||||
func (pm *PmCrypto) DecryptMIMEMessage(
|
||||
encryptedText string, verifierKey, privateKeyRing *KeyRing,
|
||||
passphrase string, callbacks MIMECallbacks, verifyTime int64,
|
||||
) {
|
||||
decsignverify, err := pm.DecryptMessageVerify(encryptedText, verifierKey, privateKeyRing, passphrase, verifyTime)
|
||||
if err != nil {
|
||||
callbacks.OnError(err)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import (
|
|||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-pm-crypto/armor"
|
||||
"github.com/ProtonMail/go-pm-crypto/constants"
|
||||
|
||||
"golang.org/x/crypto/openpgp"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
)
|
||||
|
|
@ -33,8 +35,10 @@ func (pm *PmCrypto) RandomTokenWith(size int) ([]byte, error) {
|
|||
}
|
||||
|
||||
// GetSessionFromKeyPacket gets session key no encoding in and out
|
||||
func (pm *PmCrypto) GetSessionFromKeyPacket(keyPackage []byte, privateKey *KeyRing, passphrase string) (*SymmetricKey, error) {
|
||||
|
||||
func (pm *PmCrypto) GetSessionFromKeyPacket(
|
||||
keyPackage []byte, privateKey *KeyRing, passphrase string,
|
||||
) (*SymmetricKey,
|
||||
error) {
|
||||
keyReader := bytes.NewReader(keyPackage)
|
||||
packets := packet.NewReader(keyReader)
|
||||
|
||||
|
|
@ -68,7 +72,7 @@ func (pm *PmCrypto) GetSessionFromKeyPacket(keyPackage []byte, privateKey *KeyRi
|
|||
return getSessionSplit(ek)
|
||||
}
|
||||
|
||||
// KeyPacketWithPublicKey
|
||||
// KeyPacketWithPublicKey returns binary packet from symmetric key and armored public key
|
||||
func (pm *PmCrypto) KeyPacketWithPublicKey(sessionSplit *SymmetricKey, publicKey string) ([]byte, error) {
|
||||
pubkeyRaw, err := armor.Unarmor(publicKey)
|
||||
if err != nil {
|
||||
|
|
@ -77,10 +81,13 @@ func (pm *PmCrypto) KeyPacketWithPublicKey(sessionSplit *SymmetricKey, publicKey
|
|||
return pm.KeyPacketWithPublicKeyBin(sessionSplit, pubkeyRaw)
|
||||
}
|
||||
|
||||
// KeyPacketWithPublicKeyBin
|
||||
// KeyPacketWithPublicKeyBin returns binary packet from symmetric key and binary public key
|
||||
func (pm *PmCrypto) KeyPacketWithPublicKeyBin(sessionSplit *SymmetricKey, publicKey []byte) ([]byte, error) {
|
||||
publicKeyReader := bytes.NewReader(publicKey)
|
||||
pubKeyEntries, err := openpgp.ReadKeyRing(publicKeyReader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outbuf := &bytes.Buffer{}
|
||||
|
||||
|
|
@ -117,14 +124,13 @@ func (pm *PmCrypto) KeyPacketWithPublicKeyBin(sessionSplit *SymmetricKey, public
|
|||
|
||||
if err = packet.SerializeEncryptedKey(outbuf, pub, cf, sessionSplit.Key, nil); err != nil {
|
||||
err = fmt.Errorf("pm-crypto: cannot set key: %v", err)
|
||||
return nil, errors.New("cannot set key: key ring is empty")
|
||||
return nil, err
|
||||
}
|
||||
return outbuf.Bytes(), nil
|
||||
}
|
||||
|
||||
// GetSessionFromSymmetricPacket
|
||||
// GetSessionFromSymmetricPacket extracts symmentric key from binary packet
|
||||
func (pm *PmCrypto) GetSessionFromSymmetricPacket(keyPackage []byte, password string) (*SymmetricKey, error) {
|
||||
|
||||
keyReader := bytes.NewReader(keyPackage)
|
||||
packets := packet.NewReader(keyReader)
|
||||
|
||||
|
|
@ -161,7 +167,7 @@ func (pm *PmCrypto) GetSessionFromSymmetricPacket(keyPackage []byte, password st
|
|||
return nil, errors.New("password incorrect")
|
||||
}
|
||||
|
||||
// SymmetricKeyPacketWithPassword
|
||||
// SymmetricKeyPacketWithPassword return binary packet from symmetric key and password
|
||||
func (pm *PmCrypto) SymmetricKeyPacketWithPassword(sessionSplit *SymmetricKey, password string) ([]byte, error) {
|
||||
outbuf := &bytes.Buffer{}
|
||||
|
||||
|
|
@ -188,7 +194,7 @@ func getSessionSplit(ek *packet.EncryptedKey) (*SymmetricKey, error) {
|
|||
if ek == nil {
|
||||
return nil, errors.New("can't decrypt key packet")
|
||||
}
|
||||
algo := "aes256"
|
||||
algo := constants.AES256
|
||||
for k, v := range symKeyAlgos {
|
||||
if v == ek.CipherFunc {
|
||||
algo = k
|
||||
|
|
@ -207,7 +213,7 @@ func getSessionSplit(ek *packet.EncryptedKey) (*SymmetricKey, error) {
|
|||
}
|
||||
|
||||
func getAlgo(cipher packet.CipherFunction) string {
|
||||
algo := "aes256"
|
||||
algo := constants.AES256
|
||||
for k, v := range symKeyAlgos {
|
||||
if v == cipher {
|
||||
algo = k
|
||||
|
|
|
|||
|
|
@ -3,27 +3,29 @@ package crypto
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-pm-crypto/internal"
|
||||
|
||||
"golang.org/x/crypto/openpgp"
|
||||
errors2 "golang.org/x/crypto/openpgp/errors"
|
||||
errorsPGP "golang.org/x/crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
"io"
|
||||
)
|
||||
|
||||
// SignTextDetached signs detached text type
|
||||
func (pm *PmCrypto) SignTextDetached(plainText string, privateKey *KeyRing, passphrase string, trim bool) (string, error) {
|
||||
func (pm *PmCrypto) SignTextDetached(
|
||||
plainText string, privateKey *KeyRing, passphrase string, trim bool,
|
||||
) (string, error) {
|
||||
//sign with 0x01 text
|
||||
if trim {
|
||||
plainText = internal.TrimNewlines(plainText)
|
||||
}
|
||||
|
||||
signEntity := privateKey.GetSigningEntity(passphrase)
|
||||
|
||||
if signEntity == nil {
|
||||
return "", errors.New("cannot sign message, signer key is not unlocked")
|
||||
signEntity, err := privateKey.GetSigningEntity(passphrase)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: pm.getTimeGenerator()}
|
||||
|
|
@ -42,10 +44,9 @@ func (pm *PmCrypto) SignTextDetached(plainText string, privateKey *KeyRing, pass
|
|||
// SignBinDetached Signs detached bin data using string key
|
||||
func (pm *PmCrypto) SignBinDetached(plainData []byte, privateKey *KeyRing, passphrase string) (string, error) {
|
||||
//sign with 0x00
|
||||
signEntity := privateKey.GetSigningEntity(passphrase)
|
||||
|
||||
if signEntity == nil {
|
||||
return "", errors.New("cannot sign message, singer key is not unlocked")
|
||||
signEntity, err := privateKey.GetSigningEntity(passphrase)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: pm.getTimeGenerator()}
|
||||
|
|
@ -61,15 +62,21 @@ func (pm *PmCrypto) SignBinDetached(plainData []byte, privateKey *KeyRing, passp
|
|||
return outBuf.String(), nil
|
||||
}
|
||||
|
||||
// VerifyTextDetachedSig verifies detached text - check if signature is valid using a given publicKey in binary format
|
||||
func (pm *PmCrypto) VerifyTextDetachedSig(signature string, plainText string, publicKey *KeyRing, verifyTime int64) (bool, error) {
|
||||
// VerifyTextDetachedSig verifies detached text
|
||||
// - check if signature is valid using a given publicKey in binary format
|
||||
func (pm *PmCrypto) VerifyTextDetachedSig(
|
||||
signature string, plainText string, publicKey *KeyRing, verifyTime int64,
|
||||
) (bool, error) {
|
||||
plainText = internal.TrimNewlines(plainText)
|
||||
origText := bytes.NewReader(bytes.NewBufferString(plainText).Bytes())
|
||||
|
||||
return verifySignature(publicKey.entities, origText, signature, verifyTime)
|
||||
}
|
||||
|
||||
func verifySignature(pubKeyEntries openpgp.EntityList, origText *bytes.Reader, signature string, verifyTime int64) (bool, error) {
|
||||
func verifySignature(
|
||||
pubKeyEntries openpgp.EntityList, origText *bytes.Reader,
|
||||
signature string, verifyTime int64,
|
||||
) (bool, error) {
|
||||
config := &packet.Config{}
|
||||
if verifyTime == 0 {
|
||||
config.Time = func() time.Time {
|
||||
|
|
@ -84,24 +91,26 @@ func verifySignature(pubKeyEntries openpgp.EntityList, origText *bytes.Reader, s
|
|||
|
||||
signer, err := openpgp.CheckArmoredDetachedSignature(pubKeyEntries, origText, signatureReader, config)
|
||||
|
||||
if err == errors2.ErrSignatureExpired && signer != nil {
|
||||
if verifyTime > 0 {
|
||||
if err == errorsPGP.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)
|
||||
}
|
||||
|
||||
signatureReader.Seek(0, io.SeekStart)
|
||||
_, err = signatureReader.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
signer, err = openpgp.CheckArmoredDetachedSignature(pubKeyEntries, origText, signatureReader, config)
|
||||
} else {
|
||||
// verifyTime = 0: time check disabled, everything is okay
|
||||
err = nil
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if signer == nil {
|
||||
return false, errors.New("signer is empty")
|
||||
}
|
||||
|
|
@ -112,8 +121,11 @@ func verifySignature(pubKeyEntries openpgp.EntityList, origText *bytes.Reader, s
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// VerifyBinDetachedSig verifies detached text in binary format - check if signature is valid using a given publicKey in binary format
|
||||
func (pm *PmCrypto) VerifyBinDetachedSig(signature string, plainData []byte, publicKey *KeyRing, verifyTime int64) (bool, error) {
|
||||
// VerifyBinDetachedSig verifies detached text in binary format
|
||||
// - check if signature is valid using a given publicKey in binary format
|
||||
func (pm *PmCrypto) VerifyBinDetachedSig(
|
||||
signature string, plainData []byte, publicKey *KeyRing, verifyTime int64,
|
||||
) (bool, error) {
|
||||
origText := bytes.NewReader(plainData)
|
||||
|
||||
return verifySignature(publicKey.entities, origText, signature, verifyTime)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ type SignatureCollector struct {
|
|||
verified int
|
||||
}
|
||||
|
||||
func newSignatureCollector(targetAcceptor pmmime.VisitAcceptor, keyring openpgp.KeyRing, config *packet.Config) *SignatureCollector {
|
||||
func newSignatureCollector(
|
||||
targetAcceptor pmmime.VisitAcceptor, keyring openpgp.KeyRing, config *packet.Config,
|
||||
) *SignatureCollector {
|
||||
return &SignatureCollector{
|
||||
target: targetAcceptor,
|
||||
config: config,
|
||||
|
|
@ -30,11 +32,14 @@ func newSignatureCollector(targetAcceptor pmmime.VisitAcceptor, keyring openpgp.
|
|||
}
|
||||
}
|
||||
|
||||
// Accept
|
||||
func (sc *SignatureCollector) Accept(part io.Reader, header textproto.MIMEHeader, hasPlainSibling bool, isFirst, isLast bool) (err error) {
|
||||
// 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 := pmmime.GetRawMimePart(part, "--" + params["boundary"])
|
||||
newPart, rawBody := pmmime.GetRawMimePart(part, "--"+params["boundary"])
|
||||
var multiparts []io.Reader
|
||||
var multipartHeaders []textproto.MIMEHeader
|
||||
if multiparts, multipartHeaders, err = pmmime.GetMultipartParts(newPart, params); err == nil {
|
||||
|
|
@ -48,7 +53,11 @@ func (sc *SignatureCollector) Accept(part io.Reader, header textproto.MIMEHeader
|
|||
if len(multiparts) != 2 {
|
||||
sc.verified = notSigned
|
||||
// Invalid multipart/signed format just pass along
|
||||
ioutil.ReadAll(rawBody)
|
||||
_, 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
|
||||
|
|
@ -60,11 +69,18 @@ func (sc *SignatureCollector) Accept(part io.Reader, header textproto.MIMEHeader
|
|||
// actual multipart/signed format
|
||||
err = sc.target.Accept(multiparts[0], multipartHeaders[0], hasPlainChild, true, true)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
partData, _ := ioutil.ReadAll(multiparts[1])
|
||||
decodedPart := pmmime.DecodeContentEncoding(bytes.NewReader(partData), multipartHeaders[1].Get("Content-Transfer-Encoding"))
|
||||
partData, err := ioutil.ReadAll(multiparts[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
decodedPart := pmmime.DecodeContentEncoding(
|
||||
bytes.NewReader(partData),
|
||||
multipartHeaders[1].Get("Content-Transfer-Encoding"))
|
||||
|
||||
buffer, err := ioutil.ReadAll(decodedPart)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -91,11 +107,15 @@ func (sc *SignatureCollector) Accept(part io.Reader, header textproto.MIMEHeader
|
|||
}
|
||||
return
|
||||
}
|
||||
sc.target.Accept(part, header, hasPlainSibling, isFirst, isLast)
|
||||
err = sc.target.Accept(part, header, hasPlainSibling, isFirst, isLast)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSignature
|
||||
// GetSignature collected by Accept
|
||||
func (sc SignatureCollector) GetSignature() string {
|
||||
return sc.signature
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import (
|
|||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
// EncryptWithoutIntegrity encrypts data with AES-CTR. Note: this encryption mode is not secure when stored/sent on an untrusted medium.
|
||||
// EncryptWithoutIntegrity encrypts data with AES-CTR. Note: this encryption
|
||||
// mode is not secure when stored/sent on an untrusted medium.
|
||||
func EncryptWithoutIntegrity(key, input, iv []byte) (output []byte, err error) {
|
||||
var block cipher.Block
|
||||
if block, err = aes.NewCipher(key); err != nil {
|
||||
|
|
@ -25,7 +26,8 @@ func DecryptWithoutIntegrity(key, input, iv []byte) ([]byte, error) {
|
|||
return EncryptWithoutIntegrity(key, input, iv)
|
||||
}
|
||||
|
||||
// DeriveKey derives a key from a password using scrypt. N should be set to the highest power of 2 you can derive within 100 milliseconds.
|
||||
// DeriveKey derives a key from a password using scrypt. N should be set to the
|
||||
// highest power of 2 you can derive within 100 milliseconds.
|
||||
func DeriveKey(password string, salt []byte, N int) ([]byte, error) {
|
||||
return scrypt.Key([]byte(password), salt, N, 8, 1, 32)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
var pmCrypto = PmCrypto{}
|
||||
|
||||
// GetPmCrypto
|
||||
// GetPmCrypto return global PmCrypto
|
||||
func GetPmCrypto() *PmCrypto {
|
||||
return &pmCrypto
|
||||
}
|
||||
|
|
@ -29,8 +29,8 @@ func (pm *PmCrypto) GetTime() time.Time {
|
|||
|
||||
func (pm *PmCrypto) getNow() time.Time {
|
||||
if pm.latestServerTime > 0 && !pm.latestClientTime.IsZero() {
|
||||
// Sub is monotonic, it uses a monotonic clock in this case instead of the wall clock
|
||||
extrapolate := int64(pm.latestClientTime.Sub(time.Now()).Seconds())
|
||||
// Until is monotonic, it uses a monotonic clock in this case instead of the wall clock
|
||||
extrapolate := int64(time.Until(pm.latestClientTime).Seconds())
|
||||
return time.Unix(pm.latestServerTime+extrapolate, 0)
|
||||
}
|
||||
|
||||
|
|
|
|||
2
glide.lock
generated
2
glide.lock
generated
|
|
@ -58,3 +58,5 @@ testImports:
|
|||
version: 34c6fa2dc70986bccbbffcc6130f6920a924b075
|
||||
subpackages:
|
||||
- assert
|
||||
- name: github.com/golangci/golangci-lint
|
||||
version: 901cf25e20f86b7e9dc6f73eaba5afbd0cbdc257
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue