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 ./...
|
- go test -coverprofile cover.out ./...
|
||||||
- mkdir reports
|
- mkdir reports
|
||||||
- go tool cover -html=cover.out -o reports/cover.html
|
- 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:
|
tags:
|
||||||
- coverage
|
- coverage
|
||||||
artifacts:
|
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"
|
"sync"
|
||||||
|
|
||||||
armorUtils "github.com/ProtonMail/go-pm-crypto/armor"
|
armorUtils "github.com/ProtonMail/go-pm-crypto/armor"
|
||||||
|
"github.com/ProtonMail/go-pm-crypto/constants"
|
||||||
"github.com/ProtonMail/go-pm-crypto/models"
|
"github.com/ProtonMail/go-pm-crypto/models"
|
||||||
"golang.org/x/crypto/openpgp"
|
"golang.org/x/crypto/openpgp"
|
||||||
"golang.org/x/crypto/openpgp/packet"
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EncryptedSplit when encrypt attachment
|
// AttachmentProcessor to encrypt an attachment
|
||||||
type AttachmentProcessor struct {
|
type AttachmentProcessor struct {
|
||||||
w *io.WriteCloser
|
w *io.WriteCloser
|
||||||
pipe *io.PipeWriter
|
pipe *io.PipeWriter
|
||||||
|
|
@ -26,7 +27,9 @@ type AttachmentProcessor struct {
|
||||||
|
|
||||||
// Process allows the attachment processor to write the encrypted attachment
|
// Process allows the attachment processor to write the encrypted attachment
|
||||||
func (ap *AttachmentProcessor) Process(plainData []byte) {
|
func (ap *AttachmentProcessor) Process(plainData []byte) {
|
||||||
(*ap.w).Write(plainData)
|
if _, err := (*ap.w).Write(plainData); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish attachment process
|
// Finish attachment process
|
||||||
|
|
@ -43,11 +46,12 @@ func (ap *AttachmentProcessor) Finish() (*models.EncryptedSplit, error) {
|
||||||
return ap.split, nil
|
return ap.split, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypts attachment. Takes input data and key data in binary form
|
// encryptAttachment takes input data from file
|
||||||
func (pm *PmCrypto) encryptAttachment(estimatedSize int, fileName string, publicKey *KeyRing, garbageCollector int) (*AttachmentProcessor, error) {
|
func (pm *PmCrypto) encryptAttachment(
|
||||||
|
estimatedSize int, fileName string, publicKey *KeyRing, garbageCollector int,
|
||||||
|
) (*AttachmentProcessor, error) {
|
||||||
attachmentProc := &AttachmentProcessor{}
|
attachmentProc := &AttachmentProcessor{}
|
||||||
// you can also add these one at
|
// you can also add these one at a time if you need to
|
||||||
// a time if you need to
|
|
||||||
attachmentProc.done.Add(1)
|
attachmentProc.done.Add(1)
|
||||||
attachmentProc.garbageCollector = garbageCollector
|
attachmentProc.garbageCollector = garbageCollector
|
||||||
|
|
||||||
|
|
@ -68,7 +72,7 @@ func (pm *PmCrypto) encryptAttachment(estimatedSize int, fileName string, public
|
||||||
if attachmentProc.err != nil {
|
if attachmentProc.err != nil {
|
||||||
attachmentProc.err = splitError
|
attachmentProc.err = splitError
|
||||||
}
|
}
|
||||||
split.Algo = "aes256"
|
split.Algo = constants.AES256
|
||||||
attachmentProc.split = split
|
attachmentProc.split = split
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -84,8 +88,11 @@ func (pm *PmCrypto) encryptAttachment(estimatedSize int, fileName string, public
|
||||||
return attachmentProc, nil
|
return attachmentProc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncryptAttachment encrypts attachment. Takes input data and key data in binary form
|
// EncryptAttachment encrypts attachment. Takes input data and key data in
|
||||||
func (pm *PmCrypto) EncryptAttachment(plainData []byte, fileName string, publicKey *KeyRing) (*models.EncryptedSplit, error) {
|
// binary form
|
||||||
|
func (pm *PmCrypto) EncryptAttachment(
|
||||||
|
plainData []byte, fileName string, publicKey *KeyRing,
|
||||||
|
) (*models.EncryptedSplit, error) {
|
||||||
ap, err := pm.encryptAttachment(len(plainData), fileName, publicKey, -1)
|
ap, err := pm.encryptAttachment(len(plainData), fileName, publicKey, -1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -96,12 +103,12 @@ func (pm *PmCrypto) EncryptAttachment(plainData []byte, fileName string, publicK
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return split, nil
|
return split, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncryptAttachmentLowMemory ...
|
// EncryptAttachmentLowMemory with garbage collected every megabyte
|
||||||
func (pm *PmCrypto) EncryptAttachmentLowMemory(estimatedSize int, fileName string, publicKey *KeyRing) (*AttachmentProcessor, error) {
|
func (pm *PmCrypto) EncryptAttachmentLowMemory(
|
||||||
// Garbage collect every megabyte
|
estimatedSize int, fileName string, publicKey *KeyRing,
|
||||||
|
) (*AttachmentProcessor, error) {
|
||||||
return pm.encryptAttachment(estimatedSize, fileName, publicKey, 1<<20)
|
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)
|
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
|
// DecryptAttachment takes input data and key data in binary form. The
|
||||||
func (pm *PmCrypto) DecryptAttachment(keyPacket []byte, dataPacket []byte, kr *KeyRing, passphrase string) ([]byte, error) {
|
// 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
|
privKeyEntries := kr.entities
|
||||||
|
|
||||||
if err := kr.Unlock([]byte(passphrase)); err != nil {
|
if err := kr.Unlock([]byte(passphrase)); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"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) {
|
func TestAttachmentGetKey(t *testing.T) {
|
||||||
testKeyPackets, err := ioutil.ReadFile("testdata/attachment_keypacket")
|
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)
|
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 {
|
if err != nil {
|
||||||
t.Fatal("Expected no error while decrypting attachment key, got:", err)
|
t.Fatal("Expected no error while decrypting attachment key, got:", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,15 +33,16 @@ type SymmetricKey struct {
|
||||||
const SymmetricallyEncryptedTag = 210
|
const SymmetricallyEncryptedTag = 210
|
||||||
|
|
||||||
var symKeyAlgos = map[string]packet.CipherFunction{
|
var symKeyAlgos = map[string]packet.CipherFunction{
|
||||||
"3des": packet.Cipher3DES,
|
constants.ThreeDES: packet.Cipher3DES,
|
||||||
"tripledes": packet.Cipher3DES,
|
constants.TripleDES: packet.Cipher3DES,
|
||||||
"cast5": packet.CipherCAST5,
|
constants.CAST5: packet.CipherCAST5,
|
||||||
"aes128": packet.CipherAES128,
|
constants.AES128: packet.CipherAES128,
|
||||||
"aes192": packet.CipherAES192,
|
constants.AES192: packet.CipherAES192,
|
||||||
"aes256": packet.CipherAES256,
|
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 {
|
func (sk *SymmetricKey) GetCipherFunc() packet.CipherFunction {
|
||||||
cf, ok := symKeyAlgos[sk.Algo]
|
cf, ok := symKeyAlgos[sk.Algo]
|
||||||
if ok {
|
if ok {
|
||||||
|
|
@ -108,7 +109,10 @@ func DecryptAttKey(kr *KeyRing, keyPacket string) (key *SymmetricKey, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SeparateKeyAndData from packets in a pgp session
|
// 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
|
// For info on each, see: https://golang.org/pkg/runtime/#MemStats
|
||||||
packets := packet.NewReader(r)
|
packets := packet.NewReader(r)
|
||||||
outSplit = &models.EncryptedSplit{}
|
outSplit = &models.EncryptedSplit{}
|
||||||
|
|
@ -254,21 +258,20 @@ func SetKey(kr *KeyRing, symKey *SymmetricKey) (packets string, err error) {
|
||||||
}
|
}
|
||||||
if pub == nil {
|
if pub == nil {
|
||||||
err = fmt.Errorf("pm-crypto: cannot set key: no public key available")
|
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 {
|
if err = packet.SerializeEncryptedKey(w, pub, cf, symKey.Key, nil); err != nil {
|
||||||
err = fmt.Errorf("pm-crypto: cannot set key: %v", err)
|
err = fmt.Errorf("pm-crypto: cannot set key: %v", err)
|
||||||
return
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = w.Close(); err != nil {
|
if err = w.Close(); err != nil {
|
||||||
err = fmt.Errorf("pm-crypto: cannot set key: %v", err)
|
err = fmt.Errorf("pm-crypto: cannot set key: %v", err)
|
||||||
return
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
packets = b.String()
|
return b.String(), nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsKeyExpiredBin checks if the given key is expired. Input in binary format
|
// 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)
|
return pm.IsKeyExpiredBin(rawPubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *PmCrypto) generateKey(userName string, domain string, passphrase string, keyType string, bits int,
|
func (pm *PmCrypto) generateKey(
|
||||||
prime1 []byte, prime2 []byte, prime3 []byte, prime4 []byte) (string, error) {
|
userName, domain, passphrase, keyType string,
|
||||||
|
bits int,
|
||||||
|
prime1, prime2, prime3, prime4 []byte,
|
||||||
|
) (string, error) {
|
||||||
if len(userName) <= 0 {
|
if len(userName) <= 0 {
|
||||||
return "", errors.New("invalid user name format")
|
return "", errors.New("invalid user name format")
|
||||||
}
|
}
|
||||||
|
|
@ -421,12 +426,15 @@ func (pm *PmCrypto) GenerateRSAKeyWithPrimes(
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateKey and generate primes
|
// 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)
|
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
|
// UpdatePrivateKeyPassphrase decrypts the given private key with oldPhrase and
|
||||||
func (pm *PmCrypto) UpdatePrivateKeyPassphrase(privateKey string, oldPassphrase string, newPassphrase string) (string, error) {
|
// re-encrypts with the newPassphrase
|
||||||
|
func (pm *PmCrypto) UpdatePrivateKeyPassphrase(
|
||||||
|
privateKey string, oldPassphrase string, newPassphrase string,
|
||||||
|
) (string, error) {
|
||||||
privKey := strings.NewReader(privateKey)
|
privKey := strings.NewReader(privateKey)
|
||||||
privKeyEntries, err := openpgp.ReadArmoredKeyRing(privKey)
|
privKeyEntries, err := openpgp.ReadArmoredKeyRing(privKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/go-pm-crypto/constants"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
const name = "richard.stallman"
|
const name = "richard.stallman"
|
||||||
|
|
@ -38,7 +40,7 @@ func TestGenerateKeys(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateKeyRings(t *testing.T) {
|
func TestGenerateKeyRings(t *testing.T) {
|
||||||
rsaPrivateKeyRing, err = ReadArmoredKeyRing(strings.NewReader(rsaKey));
|
rsaPrivateKeyRing, err = ReadArmoredKeyRing(strings.NewReader(rsaKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Cannot read RSA key:", err)
|
t.Fatal("Cannot read RSA key:", err)
|
||||||
}
|
}
|
||||||
|
|
@ -48,7 +50,7 @@ func TestGenerateKeyRings(t *testing.T) {
|
||||||
t.Fatal("Cannot extract RSA public key:", err)
|
t.Fatal("Cannot extract RSA public key:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rsaPublicKeyRing, err = ReadArmoredKeyRing(strings.NewReader(rsaPublicKey));
|
rsaPublicKeyRing, err = ReadArmoredKeyRing(strings.NewReader(rsaPublicKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Cannot read RSA public key:", err)
|
t.Fatal("Cannot read RSA public key:", err)
|
||||||
}
|
}
|
||||||
|
|
@ -58,7 +60,7 @@ func TestGenerateKeyRings(t *testing.T) {
|
||||||
t.Fatal("Cannot decrypt RSA key:", err)
|
t.Fatal("Cannot decrypt RSA key:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ecPrivateKeyRing, err = ReadArmoredKeyRing(strings.NewReader(ecKey));
|
ecPrivateKeyRing, err = ReadArmoredKeyRing(strings.NewReader(ecKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Cannot read EC key:", err)
|
t.Fatal("Cannot read EC key:", err)
|
||||||
}
|
}
|
||||||
|
|
@ -68,7 +70,7 @@ func TestGenerateKeyRings(t *testing.T) {
|
||||||
t.Fatal("Cannot extract EC public key:", err)
|
t.Fatal("Cannot extract EC public key:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ecPublicKeyRing, err = ReadArmoredKeyRing(strings.NewReader(ecPublicKey));
|
ecPublicKeyRing, err = ReadArmoredKeyRing(strings.NewReader(ecPublicKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Cannot read EC public key:", err)
|
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 pass, _ = base64.StdEncoding.DecodeString("H2CAwzpdexjxXucVYMERDiAc/td8aGPrr6ZhfMnZlLI=")
|
||||||
var testSymmetricKey = &SymmetricKey{
|
var testSymmetricKey = &SymmetricKey{
|
||||||
Key: pass,
|
Key: pass,
|
||||||
Algo: "aes256",
|
Algo: constants.AES256,
|
||||||
}
|
}
|
||||||
|
|
||||||
packet, err := SetKey(rsaPublicKeyRing, testSymmetricKey)
|
packet, err := SetKey(rsaPublicKeyRing, testSymmetricKey)
|
||||||
|
|
@ -122,8 +124,9 @@ func TestUpdatePrivateKeysPassphrase(t *testing.T) {
|
||||||
passphrase = newPassphrase
|
passphrase = newPassphrase
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleCheckKeys() {
|
// ExampleCheckKey to track changes in test data
|
||||||
pmCrypto.CheckKey(readTestFile("keyring_publicKey"))
|
func ExampleCheckKey() {
|
||||||
|
_, _ = pmCrypto.CheckKey(readTestFile("keyring_publicKey"))
|
||||||
// Output:
|
// Output:
|
||||||
// SubKey:37e4bcf09b36e34012d10c0247dc67b5cb8267f6
|
// SubKey:37e4bcf09b36e34012d10c0247dc67b5cb8267f6
|
||||||
// PrimaryKey:6e8ba229b0cccaf6962f97953eb6259edf21df24
|
// PrimaryKey:6e8ba229b0cccaf6962f97953eb6259edf21df24
|
||||||
|
|
|
||||||
|
|
@ -115,22 +115,28 @@ func (kr *KeyRing) GetEntities() openpgp.EntityList {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSigningEntity returns first private signing entity from keyring
|
// 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
|
var signEntity *openpgp.Entity
|
||||||
|
|
||||||
for _, e := range kr.entities {
|
for _, e := range kr.entities {
|
||||||
// Entity.PrivateKey must be a signing key
|
// Entity.PrivateKey must be a signing key
|
||||||
if e.PrivateKey != nil {
|
if e.PrivateKey != nil {
|
||||||
if e.PrivateKey.Encrypted {
|
if e.PrivateKey.Encrypted {
|
||||||
e.PrivateKey.Decrypt([]byte(passphrase))
|
if err := e.PrivateKey.Decrypt([]byte(passphrase)); err != nil {
|
||||||
}
|
continue
|
||||||
if !e.PrivateKey.Encrypted {
|
}
|
||||||
|
|
||||||
signEntity = e
|
signEntity = e
|
||||||
break
|
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
|
// 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
|
// 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}
|
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: timeGenerator}
|
||||||
|
|
||||||
hints := &openpgp.FileHints{
|
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
|
// 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
|
var encryptedWriter io.WriteCloser
|
||||||
buffer := &bytes.Buffer{}
|
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),
|
// 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
|
// contents are still provided if library clients wish to process this message further
|
||||||
func (kr *KeyRing) DecryptStringIfNeeded(data string) (decrypted string, err error) {
|
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
|
var signed SignedString
|
||||||
signed, err = kr.DecryptString(data)
|
signed, err = kr.DecryptString(data)
|
||||||
decrypted = signed.String
|
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)
|
err = openpgp.DetachSign(w, signEntity, toSign, config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
return err
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyString may return errors.ErrSignatureExpired (defined in
|
// 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
|
// Entity.Subkeys can be used for encryption
|
||||||
for _, subKey := range e.Subkeys {
|
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)
|
keys = append(keys, subKey.PrivateKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -516,10 +532,8 @@ func (kr *KeyRing) CheckPassphrase(passphrase string) bool {
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if n == 0 {
|
|
||||||
return false
|
return n != 0
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// readFrom reads unarmored and armored keys from r and adds them to the keyring.
|
// 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) {
|
switch entity.PrivateKey.PrivateKey.(type) {
|
||||||
// TODO: type mismatch after crypto lib update, fix this:
|
// TODO: type mismatch after crypto lib update, fix this:
|
||||||
case *rsa.PrivateKey:
|
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:
|
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 {
|
for _, subkey := range entity.Subkeys {
|
||||||
if subkey.PrivateKey != nil {
|
if subkey.PrivateKey != nil {
|
||||||
switch subkey.PrivateKey.PrivateKey.(type) {
|
switch subkey.PrivateKey.PrivateKey.(type) {
|
||||||
case *rsa.PrivateKey:
|
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:
|
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 {
|
if i == 0 {
|
||||||
kr.FirstKeyID = ko.ID
|
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.
|
// 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) {
|
func FilterExpiredKeys(contactKeys []*KeyRing) (filteredKeys []*KeyRing, err error) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
hasExpiredEntity := false
|
hasExpiredEntity := false
|
||||||
filteredKeys = make([]*KeyRing, 0, 0)
|
filteredKeys = make([]*KeyRing, 0)
|
||||||
|
|
||||||
for _, contactKeyRing := range contactKeys {
|
for _, contactKeyRing := range contactKeys {
|
||||||
keyRingHasUnexpiredEntity := false
|
keyRingHasUnexpiredEntity := false
|
||||||
|
|
@ -688,5 +715,5 @@ func FilterExpiredKeys(contactKeys []*KeyRing) (filteredKeys []*KeyRing, err err
|
||||||
return filteredKeys, errors.New("all contacts keys are expired")
|
return filteredKeys, errors.New("all contacts keys are expired")
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return filteredKeys, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,29 @@
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"golang.org/x/crypto/openpgp/armor"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"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{
|
var testSymmetricKey = &SymmetricKey{
|
||||||
Key: decodedSymmetricKey,
|
Key: decodedSymmetricKey,
|
||||||
Algo: "aes256",
|
Algo: constants.AES256,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Corresponding key in testdata/keyring_privateKey
|
// Corresponding key in testdata/keyring_privateKey
|
||||||
const testMailboxPassword = "apple"
|
const testMailboxPassword = "apple"
|
||||||
|
|
||||||
// Corresponding key in testdata/keyring_privateKeyLegacy
|
// Corresponding key in testdata/keyring_privateKeyLegacy
|
||||||
const testMailboxPasswordLegacy = "123"
|
// const testMailboxPasswordLegacy = "123"
|
||||||
|
|
||||||
const testToken = "d79ca194a22810a5363eeddfdef7dfbc327c6229"
|
const testToken = "d79ca194a22810a5363eeddfdef7dfbc327c6229"
|
||||||
|
|
||||||
|
|
@ -29,22 +32,25 @@ var (
|
||||||
testPublicKeyRing *KeyRing
|
testPublicKeyRing *KeyRing
|
||||||
)
|
)
|
||||||
|
|
||||||
var testIdentity = &Identity{
|
// var testIdentity = &Identity{
|
||||||
Name: "UserID",
|
// Name: "UserID",
|
||||||
Email: "",
|
// Email: "",
|
||||||
}
|
// }
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var err error
|
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)
|
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)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := testPrivateKeyRing.Unlock([]byte(testMailboxPassword)); err != nil {
|
err = testPrivateKeyRing.Unlock([]byte(testMailboxPassword))
|
||||||
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -20,11 +19,13 @@ import (
|
||||||
"github.com/ProtonMail/go-pm-crypto/models"
|
"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
|
// encryptedText : string armored encrypted
|
||||||
// privateKey : armored private use to decrypt message
|
// privateKey : armored private use to decrypt message
|
||||||
// passphrase : match with private key 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)
|
privKeyRaw, err := armorUtils.Unarmor(privateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
@ -58,7 +59,11 @@ func (pm *PmCrypto) DecryptMessage(encryptedText string, privateKey *KeyRing, pa
|
||||||
return string(b), nil
|
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)
|
rawPwd := []byte(passphrase)
|
||||||
if err := privKey.Unlock(rawPwd); err != nil {
|
if err := privKey.Unlock(rawPwd); err != nil {
|
||||||
err = fmt.Errorf("pm-crypto: cannot decrypt passphrase: %v", err)
|
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
|
privKeyEntries := privKey.entities
|
||||||
for _, entity := range privKey.entities {
|
|
||||||
privKeyEntries = append(privKeyEntries, entity)
|
|
||||||
}
|
|
||||||
|
|
||||||
if additionalEntries != nil {
|
if additionalEntries != nil {
|
||||||
for _, e := range additionalEntries {
|
privKeyEntries = append(privKeyEntries, additionalEntries...)
|
||||||
privKeyEntries = append(privKeyEntries, e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptedio, err := internal.Unarmor(encryptedText)
|
encryptedio, err := internal.Unarmor(encryptedText)
|
||||||
|
|
@ -92,7 +92,10 @@ func decryptCore(encryptedText string, additionalEntries openpgp.EntityList, pri
|
||||||
// verifierKey []byte: unarmored verifier keys
|
// verifierKey []byte: unarmored verifier keys
|
||||||
// privateKeyRing []byte: unarmored private key to decrypt. could be multiple
|
// privateKeyRing []byte: unarmored private key to decrypt. could be multiple
|
||||||
// passphrase: match with private key to decrypt message
|
// 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 := &models.DecryptSignedVerify{}
|
||||||
out.Verify = failed
|
out.Verify = failed
|
||||||
|
|
||||||
|
|
@ -101,7 +104,16 @@ func (pm *PmCrypto) DecryptMessageVerify(encryptedText string, verifierKey *KeyR
|
||||||
out.Verify = noVerifier
|
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
|
decrypted := md.UnverifiedBody
|
||||||
b, err := ioutil.ReadAll(decrypted)
|
b, err := ioutil.ReadAll(decrypted)
|
||||||
|
|
@ -136,7 +148,8 @@ func (pm *PmCrypto) DecryptMessageVerify(encryptedText string, verifierKey *KeyR
|
||||||
return out, nil
|
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) {
|
func processSignatureExpiration(md *openpgp.MessageDetails, verifyTime int64) {
|
||||||
if md.SignatureError == pgpErrors.ErrSignatureExpired {
|
if md.SignatureError == pgpErrors.ErrSignatureExpired {
|
||||||
if verifyTime > 0 {
|
if verifyTime > 0 {
|
||||||
|
|
@ -159,7 +172,6 @@ func processSignatureExpiration(md *openpgp.MessageDetails, verifyTime int64) {
|
||||||
// plainText string: clear text
|
// plainText string: clear text
|
||||||
// output string: armored pgp message
|
// output string: armored pgp message
|
||||||
func (pm *PmCrypto) EncryptMessageWithPassword(plainText string, password string) (string, error) {
|
func (pm *PmCrypto) EncryptMessageWithPassword(plainText string, password string) (string, error) {
|
||||||
|
|
||||||
var outBuf bytes.Buffer
|
var outBuf bytes.Buffer
|
||||||
w, err := armor.Encode(&outBuf, constants.PGPMessageHeader, internal.ArmorHeaders)
|
w, err := armor.Encode(&outBuf, constants.PGPMessageHeader, internal.ArmorHeaders)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -185,14 +197,17 @@ func (pm *PmCrypto) EncryptMessageWithPassword(plainText string, password string
|
||||||
return outBuf.String(), nil
|
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
|
// publicKey : bytes unarmored public key
|
||||||
// plainText : the input
|
// plainText : the input
|
||||||
// privateKey : optional required when you want to sign
|
// 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
|
// 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
|
// 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 {
|
if trim {
|
||||||
plainText = internal.TrimNewlines(plainText)
|
plainText = internal.TrimNewlines(plainText)
|
||||||
}
|
}
|
||||||
|
|
@ -205,20 +220,22 @@ func (pm *PmCrypto) EncryptMessage(plainText string, publicKey *KeyRing, private
|
||||||
var signEntity *openpgp.Entity
|
var signEntity *openpgp.Entity
|
||||||
|
|
||||||
if len(passphrase) > 0 && len(privateKey.entities) > 0 {
|
if len(passphrase) > 0 && len(privateKey.entities) > 0 {
|
||||||
|
var err error
|
||||||
signEntity := privateKey.GetSigningEntity(passphrase)
|
signEntity, err = privateKey.GetSigningEntity(passphrase)
|
||||||
|
if err != nil {
|
||||||
if signEntity == nil {
|
return "", err
|
||||||
return "", errors.New("cannot sign message, signer key is not unlocked")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ew, err := EncryptCore(w, publicKey.entities, signEntity, "", false, pm.getTimeGenerator())
|
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()
|
ew.Close()
|
||||||
w.Close()
|
w.Close()
|
||||||
return outBuf.String(), nil
|
return outBuf.String(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecryptMessageWithPassword decrypts a pgp message with a password
|
// DecryptMessageWithPassword decrypts a pgp message with a password
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@ import (
|
||||||
"golang.org/x/crypto/openpgp/packet"
|
"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))
|
mm, err := mail.ReadMessage(strings.NewReader(mimeBody))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, nil, err
|
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)
|
h := textproto.MIMEHeader(mm.Header)
|
||||||
mmBodyData, err := ioutil.ReadAll(mm.Body)
|
mmBodyData, err := ioutil.ReadAll(mm.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
printAccepter := pmmime.NewMIMEPrinter()
|
printAccepter := pmmime.NewMIMEPrinter()
|
||||||
bodyCollector := pmmime.NewBodyCollector(printAccepter)
|
bodyCollector := pmmime.NewBodyCollector(printAccepter)
|
||||||
|
|
@ -42,10 +47,10 @@ func (pm PmCrypto) parseMIME(mimeBody string, verifierKey *KeyRing) (*pmmime.Bod
|
||||||
atts := attachmentsCollector.GetAttachments()
|
atts := attachmentsCollector.GetAttachments()
|
||||||
attHeaders := attachmentsCollector.GetAttHeaders()
|
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 {
|
type MIMECallbacks interface {
|
||||||
OnBody(body string, mimetype string)
|
OnBody(body string, mimetype string)
|
||||||
OnAttachment(headers string, data []byte)
|
OnAttachment(headers string, data []byte)
|
||||||
|
|
@ -56,8 +61,10 @@ type MIMECallbacks interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecryptMIMEMessage decrypts a MIME message
|
// DecryptMIMEMessage decrypts a MIME message
|
||||||
func (pm *PmCrypto) DecryptMIMEMessage(encryptedText string, verifierKey *KeyRing, privateKeyRing *KeyRing,
|
func (pm *PmCrypto) DecryptMIMEMessage(
|
||||||
passphrase string, callbacks MIMECallbacks, verifyTime int64) {
|
encryptedText string, verifierKey, privateKeyRing *KeyRing,
|
||||||
|
passphrase string, callbacks MIMECallbacks, verifyTime int64,
|
||||||
|
) {
|
||||||
decsignverify, err := pm.DecryptMessageVerify(encryptedText, verifierKey, privateKeyRing, passphrase, verifyTime)
|
decsignverify, err := pm.DecryptMessageVerify(encryptedText, verifierKey, privateKeyRing, passphrase, verifyTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
callbacks.OnError(err)
|
callbacks.OnError(err)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/ProtonMail/go-pm-crypto/armor"
|
"github.com/ProtonMail/go-pm-crypto/armor"
|
||||||
|
"github.com/ProtonMail/go-pm-crypto/constants"
|
||||||
|
|
||||||
"golang.org/x/crypto/openpgp"
|
"golang.org/x/crypto/openpgp"
|
||||||
"golang.org/x/crypto/openpgp/packet"
|
"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
|
// 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)
|
keyReader := bytes.NewReader(keyPackage)
|
||||||
packets := packet.NewReader(keyReader)
|
packets := packet.NewReader(keyReader)
|
||||||
|
|
||||||
|
|
@ -68,7 +72,7 @@ func (pm *PmCrypto) GetSessionFromKeyPacket(keyPackage []byte, privateKey *KeyRi
|
||||||
return getSessionSplit(ek)
|
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) {
|
func (pm *PmCrypto) KeyPacketWithPublicKey(sessionSplit *SymmetricKey, publicKey string) ([]byte, error) {
|
||||||
pubkeyRaw, err := armor.Unarmor(publicKey)
|
pubkeyRaw, err := armor.Unarmor(publicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -77,10 +81,13 @@ func (pm *PmCrypto) KeyPacketWithPublicKey(sessionSplit *SymmetricKey, publicKey
|
||||||
return pm.KeyPacketWithPublicKeyBin(sessionSplit, pubkeyRaw)
|
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) {
|
func (pm *PmCrypto) KeyPacketWithPublicKeyBin(sessionSplit *SymmetricKey, publicKey []byte) ([]byte, error) {
|
||||||
publicKeyReader := bytes.NewReader(publicKey)
|
publicKeyReader := bytes.NewReader(publicKey)
|
||||||
pubKeyEntries, err := openpgp.ReadKeyRing(publicKeyReader)
|
pubKeyEntries, err := openpgp.ReadKeyRing(publicKeyReader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
outbuf := &bytes.Buffer{}
|
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 {
|
if err = packet.SerializeEncryptedKey(outbuf, pub, cf, sessionSplit.Key, nil); err != nil {
|
||||||
err = fmt.Errorf("pm-crypto: cannot set key: %v", err)
|
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
|
return outbuf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSessionFromSymmetricPacket
|
// GetSessionFromSymmetricPacket extracts symmentric key from binary packet
|
||||||
func (pm *PmCrypto) GetSessionFromSymmetricPacket(keyPackage []byte, password string) (*SymmetricKey, error) {
|
func (pm *PmCrypto) GetSessionFromSymmetricPacket(keyPackage []byte, password string) (*SymmetricKey, error) {
|
||||||
|
|
||||||
keyReader := bytes.NewReader(keyPackage)
|
keyReader := bytes.NewReader(keyPackage)
|
||||||
packets := packet.NewReader(keyReader)
|
packets := packet.NewReader(keyReader)
|
||||||
|
|
||||||
|
|
@ -161,7 +167,7 @@ func (pm *PmCrypto) GetSessionFromSymmetricPacket(keyPackage []byte, password st
|
||||||
return nil, errors.New("password incorrect")
|
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) {
|
func (pm *PmCrypto) SymmetricKeyPacketWithPassword(sessionSplit *SymmetricKey, password string) ([]byte, error) {
|
||||||
outbuf := &bytes.Buffer{}
|
outbuf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
|
@ -188,7 +194,7 @@ func getSessionSplit(ek *packet.EncryptedKey) (*SymmetricKey, error) {
|
||||||
if ek == nil {
|
if ek == nil {
|
||||||
return nil, errors.New("can't decrypt key packet")
|
return nil, errors.New("can't decrypt key packet")
|
||||||
}
|
}
|
||||||
algo := "aes256"
|
algo := constants.AES256
|
||||||
for k, v := range symKeyAlgos {
|
for k, v := range symKeyAlgos {
|
||||||
if v == ek.CipherFunc {
|
if v == ek.CipherFunc {
|
||||||
algo = k
|
algo = k
|
||||||
|
|
@ -207,7 +213,7 @@ func getSessionSplit(ek *packet.EncryptedKey) (*SymmetricKey, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAlgo(cipher packet.CipherFunction) string {
|
func getAlgo(cipher packet.CipherFunction) string {
|
||||||
algo := "aes256"
|
algo := constants.AES256
|
||||||
for k, v := range symKeyAlgos {
|
for k, v := range symKeyAlgos {
|
||||||
if v == cipher {
|
if v == cipher {
|
||||||
algo = k
|
algo = k
|
||||||
|
|
|
||||||
|
|
@ -3,27 +3,29 @@ package crypto
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ProtonMail/go-pm-crypto/internal"
|
"github.com/ProtonMail/go-pm-crypto/internal"
|
||||||
|
|
||||||
"golang.org/x/crypto/openpgp"
|
"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"
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
"io"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignTextDetached signs detached text type
|
// 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
|
//sign with 0x01 text
|
||||||
if trim {
|
if trim {
|
||||||
plainText = internal.TrimNewlines(plainText)
|
plainText = internal.TrimNewlines(plainText)
|
||||||
}
|
}
|
||||||
|
|
||||||
signEntity := privateKey.GetSigningEntity(passphrase)
|
signEntity, err := privateKey.GetSigningEntity(passphrase)
|
||||||
|
if err != nil {
|
||||||
if signEntity == nil {
|
return "", err
|
||||||
return "", errors.New("cannot sign message, signer key is not unlocked")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: pm.getTimeGenerator()}
|
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
|
// SignBinDetached Signs detached bin data using string key
|
||||||
func (pm *PmCrypto) SignBinDetached(plainData []byte, privateKey *KeyRing, passphrase string) (string, error) {
|
func (pm *PmCrypto) SignBinDetached(plainData []byte, privateKey *KeyRing, passphrase string) (string, error) {
|
||||||
//sign with 0x00
|
//sign with 0x00
|
||||||
signEntity := privateKey.GetSigningEntity(passphrase)
|
signEntity, err := privateKey.GetSigningEntity(passphrase)
|
||||||
|
if err != nil {
|
||||||
if signEntity == nil {
|
return "", err
|
||||||
return "", errors.New("cannot sign message, singer key is not unlocked")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: pm.getTimeGenerator()}
|
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
|
return outBuf.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyTextDetachedSig verifies detached text - check if signature is valid using a given publicKey in binary format
|
// VerifyTextDetachedSig verifies detached text
|
||||||
func (pm *PmCrypto) VerifyTextDetachedSig(signature string, plainText string, publicKey *KeyRing, verifyTime int64) (bool, error) {
|
// - 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)
|
plainText = internal.TrimNewlines(plainText)
|
||||||
origText := bytes.NewReader(bytes.NewBufferString(plainText).Bytes())
|
origText := bytes.NewReader(bytes.NewBufferString(plainText).Bytes())
|
||||||
|
|
||||||
return verifySignature(publicKey.entities, origText, signature, verifyTime)
|
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{}
|
config := &packet.Config{}
|
||||||
if verifyTime == 0 {
|
if verifyTime == 0 {
|
||||||
config.Time = func() time.Time {
|
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)
|
signer, err := openpgp.CheckArmoredDetachedSignature(pubKeyEntries, origText, signatureReader, config)
|
||||||
|
|
||||||
if err == errors2.ErrSignatureExpired && signer != nil {
|
if err == errorsPGP.ErrSignatureExpired && signer != nil {
|
||||||
if verifyTime > 0 {
|
if verifyTime > 0 { // if verifyTime = 0: time check disabled, everything is okay
|
||||||
// Maybe the creation time offset pushed it over the edge
|
// Maybe the creation time offset pushed it over the edge
|
||||||
// Retry with the actual verification time
|
// Retry with the actual verification time
|
||||||
config.Time = func() time.Time {
|
config.Time = func() time.Time {
|
||||||
return time.Unix(verifyTime, 0)
|
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)
|
signer, err = openpgp.CheckArmoredDetachedSignature(pubKeyEntries, origText, signatureReader, config)
|
||||||
} else {
|
if err != nil {
|
||||||
// verifyTime = 0: time check disabled, everything is okay
|
return false, err
|
||||||
err = nil
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if signer == nil {
|
if signer == nil {
|
||||||
return false, errors.New("signer is empty")
|
return false, errors.New("signer is empty")
|
||||||
}
|
}
|
||||||
|
|
@ -112,8 +121,11 @@ func verifySignature(pubKeyEntries openpgp.EntityList, origText *bytes.Reader, s
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyBinDetachedSig verifies detached text in binary format - check if signature is valid using a given publicKey in binary format
|
// VerifyBinDetachedSig verifies detached text in binary format
|
||||||
func (pm *PmCrypto) VerifyBinDetachedSig(signature string, plainData []byte, publicKey *KeyRing, verifyTime int64) (bool, error) {
|
// - 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)
|
origText := bytes.NewReader(plainData)
|
||||||
|
|
||||||
return verifySignature(publicKey.entities, origText, signature, verifyTime)
|
return verifySignature(publicKey.entities, origText, signature, verifyTime)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,9 @@ type SignatureCollector struct {
|
||||||
verified int
|
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{
|
return &SignatureCollector{
|
||||||
target: targetAcceptor,
|
target: targetAcceptor,
|
||||||
config: config,
|
config: config,
|
||||||
|
|
@ -30,11 +32,14 @@ func newSignatureCollector(targetAcceptor pmmime.VisitAcceptor, keyring openpgp.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accept
|
// Accept collects the signature
|
||||||
func (sc *SignatureCollector) Accept(part io.Reader, header textproto.MIMEHeader, hasPlainSibling bool, isFirst, isLast bool) (err error) {
|
func (sc *SignatureCollector) Accept(
|
||||||
|
part io.Reader, header textproto.MIMEHeader,
|
||||||
|
hasPlainSibling, isFirst, isLast bool,
|
||||||
|
) (err error) {
|
||||||
parentMediaType, params, _ := mime.ParseMediaType(header.Get("Content-Type"))
|
parentMediaType, params, _ := mime.ParseMediaType(header.Get("Content-Type"))
|
||||||
if parentMediaType == "multipart/signed" {
|
if parentMediaType == "multipart/signed" {
|
||||||
newPart, rawBody := pmmime.GetRawMimePart(part, "--" + params["boundary"])
|
newPart, rawBody := pmmime.GetRawMimePart(part, "--"+params["boundary"])
|
||||||
var multiparts []io.Reader
|
var multiparts []io.Reader
|
||||||
var multipartHeaders []textproto.MIMEHeader
|
var multipartHeaders []textproto.MIMEHeader
|
||||||
if multiparts, multipartHeaders, err = pmmime.GetMultipartParts(newPart, params); err == nil {
|
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 {
|
if len(multiparts) != 2 {
|
||||||
sc.verified = notSigned
|
sc.verified = notSigned
|
||||||
// Invalid multipart/signed format just pass along
|
// Invalid multipart/signed format just pass along
|
||||||
ioutil.ReadAll(rawBody)
|
_, err = ioutil.ReadAll(rawBody)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for i, p := range multiparts {
|
for i, p := range multiparts {
|
||||||
if err = sc.target.Accept(p, multipartHeaders[i], hasPlainChild, true, true); err != nil {
|
if err = sc.target.Accept(p, multipartHeaders[i], hasPlainChild, true, true); err != nil {
|
||||||
return
|
return
|
||||||
|
|
@ -60,11 +69,18 @@ func (sc *SignatureCollector) Accept(part io.Reader, header textproto.MIMEHeader
|
||||||
// actual multipart/signed format
|
// actual multipart/signed format
|
||||||
err = sc.target.Accept(multiparts[0], multipartHeaders[0], hasPlainChild, true, true)
|
err = sc.target.Accept(multiparts[0], multipartHeaders[0], hasPlainChild, true, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
partData, _ := ioutil.ReadAll(multiparts[1])
|
partData, err := ioutil.ReadAll(multiparts[1])
|
||||||
decodedPart := pmmime.DecodeContentEncoding(bytes.NewReader(partData), multipartHeaders[1].Get("Content-Transfer-Encoding"))
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
decodedPart := pmmime.DecodeContentEncoding(
|
||||||
|
bytes.NewReader(partData),
|
||||||
|
multipartHeaders[1].Get("Content-Transfer-Encoding"))
|
||||||
|
|
||||||
buffer, err := ioutil.ReadAll(decodedPart)
|
buffer, err := ioutil.ReadAll(decodedPart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -91,11 +107,15 @@ func (sc *SignatureCollector) Accept(part io.Reader, header textproto.MIMEHeader
|
||||||
}
|
}
|
||||||
return
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSignature
|
// GetSignature collected by Accept
|
||||||
func (sc SignatureCollector) GetSignature() string {
|
func (sc SignatureCollector) GetSignature() string {
|
||||||
return sc.signature
|
return sc.signature
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ import (
|
||||||
"golang.org/x/crypto/scrypt"
|
"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) {
|
func EncryptWithoutIntegrity(key, input, iv []byte) (output []byte, err error) {
|
||||||
var block cipher.Block
|
var block cipher.Block
|
||||||
if block, err = aes.NewCipher(key); err != nil {
|
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)
|
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) {
|
func DeriveKey(password string, salt []byte, N int) ([]byte, error) {
|
||||||
return scrypt.Key([]byte(password), salt, N, 8, 1, 32)
|
return scrypt.Key([]byte(password), salt, N, 8, 1, 32)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
var pmCrypto = PmCrypto{}
|
var pmCrypto = PmCrypto{}
|
||||||
|
|
||||||
// GetPmCrypto
|
// GetPmCrypto return global PmCrypto
|
||||||
func GetPmCrypto() *PmCrypto {
|
func GetPmCrypto() *PmCrypto {
|
||||||
return &pmCrypto
|
return &pmCrypto
|
||||||
}
|
}
|
||||||
|
|
@ -29,8 +29,8 @@ func (pm *PmCrypto) GetTime() time.Time {
|
||||||
|
|
||||||
func (pm *PmCrypto) getNow() time.Time {
|
func (pm *PmCrypto) getNow() time.Time {
|
||||||
if pm.latestServerTime > 0 && !pm.latestClientTime.IsZero() {
|
if pm.latestServerTime > 0 && !pm.latestClientTime.IsZero() {
|
||||||
// Sub is monotonic, it uses a monotonic clock in this case instead of the wall clock
|
// Until is monotonic, it uses a monotonic clock in this case instead of the wall clock
|
||||||
extrapolate := int64(pm.latestClientTime.Sub(time.Now()).Seconds())
|
extrapolate := int64(time.Until(pm.latestClientTime).Seconds())
|
||||||
return time.Unix(pm.latestServerTime+extrapolate, 0)
|
return time.Unix(pm.latestServerTime+extrapolate, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
glide.lock
generated
2
glide.lock
generated
|
|
@ -58,3 +58,5 @@ testImports:
|
||||||
version: 34c6fa2dc70986bccbbffcc6130f6920a924b075
|
version: 34c6fa2dc70986bccbbffcc6130f6920a924b075
|
||||||
subpackages:
|
subpackages:
|
||||||
- assert
|
- assert
|
||||||
|
- name: github.com/golangci/golangci-lint
|
||||||
|
version: 901cf25e20f86b7e9dc6f73eaba5afbd0cbdc257
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue