From c610c633a807649bf12e774eb479b9d8c440c67d Mon Sep 17 00:00:00 2001 From: Brandon McDonnell Date: Mon, 9 Jul 2018 10:52:18 -0400 Subject: [PATCH 1/3] protonmail: Fetch and decrypt all account keys. --- protonmail/auth.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/protonmail/auth.go b/protonmail/auth.go index 1a59bde..7543ca4 100644 --- a/protonmail/auth.go +++ b/protonmail/auth.go @@ -260,6 +260,51 @@ func (c *Client) Unlock(auth *Auth, passphrase string) (openpgp.EntityList, erro c.uid = auth.UID c.accessToken = string(accessTokenBytes) c.keyRing = keyRing + + // Get additional private keys + u, err := c.GetCurrentUser() + if err != nil { + return nil, err + } + var keyRing2 openpgp.EntityList + for _, e := range u.Addresses { + for _, f := range e.Keys { + prKey, err := openpgp.ReadArmoredKeyRing(strings.NewReader(f.PrivateKey)) + //pkey, err := f.Entity() + if err != nil { + return nil, err + } + if len(prKey) == 0 { + return nil, errors.New("auth key ring is empty") + } + keyRing2 = append(keyRing2, prKey[0]) + + //fmt.Println(e.Email, " key ", pkey.PrimaryKey.KeyId) + //if pkey.PrimaryKey.KeyId != keyRing[0].PrimaryKey.KeyId { + // keyRing = append(keyRing, pkey) + //} + } + } + for _, e := range keyRing2 { + var privateKeys []*packet.PrivateKey + if e.PrivateKey != nil { + privateKeys = append(privateKeys, e.PrivateKey) + } + + for _, subkey := range e.Subkeys { + if subkey.PrivateKey != nil { + privateKeys = append(privateKeys, subkey.PrivateKey) + } + } + + for _, priv := range privateKeys { + if err := priv.Decrypt(passphraseBytes); err != nil { + return nil, err + } + } + keyRing = append(keyRing, e) + } + return keyRing, nil } From 7b92deb6ac2724221004ec93caee16f979702715 Mon Sep 17 00:00:00 2001 From: Brandon McDonnell Date: Mon, 9 Jul 2018 11:06:30 -0400 Subject: [PATCH 2/3] protonmail: Do not duplicate primary account key in keyring. Probably not worth the extra overhead the way I did it, but neater. --- protonmail/auth.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/protonmail/auth.go b/protonmail/auth.go index 7543ca4..1e57210 100644 --- a/protonmail/auth.go +++ b/protonmail/auth.go @@ -269,20 +269,22 @@ func (c *Client) Unlock(auth *Auth, passphrase string) (openpgp.EntityList, erro var keyRing2 openpgp.EntityList for _, e := range u.Addresses { for _, f := range e.Keys { - prKey, err := openpgp.ReadArmoredKeyRing(strings.NewReader(f.PrivateKey)) - //pkey, err := f.Entity() + pkey, err := f.Entity() if err != nil { return nil, err } + if pkey.PrimaryKey.KeyId == keyRing[0].PrimaryKey.KeyId { + continue + } + prKey, err := openpgp.ReadArmoredKeyRing(strings.NewReader(f.PrivateKey)) + // TODO Are these errors fatal? + if err != nil { + continue + } if len(prKey) == 0 { - return nil, errors.New("auth key ring is empty") + continue } keyRing2 = append(keyRing2, prKey[0]) - - //fmt.Println(e.Email, " key ", pkey.PrimaryKey.KeyId) - //if pkey.PrimaryKey.KeyId != keyRing[0].PrimaryKey.KeyId { - // keyRing = append(keyRing, pkey) - //} } } for _, e := range keyRing2 { From 0b3c3ddde58badbe33a2cbaf0c8e9a02bca70047 Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 28 Oct 2018 16:52:57 +0100 Subject: [PATCH 3/3] Address review comments --- protonmail/auth.go | 98 ++++++++++++++++++++++------------------------ protonmail/keys.go | 10 ++++- 2 files changed, 56 insertions(+), 52 deletions(-) diff --git a/protonmail/auth.go b/protonmail/auth.go index 1e57210..6d804da 100644 --- a/protonmail/auth.go +++ b/protonmail/auth.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "errors" "io/ioutil" + "log" "net/http" "strings" "time" @@ -193,6 +194,30 @@ func (c *Client) AuthRefresh(expiredAuth *Auth) (*Auth, error) { return auth, nil } +func unlockKey(e *openpgp.Entity, passphraseBytes []byte) error { + var privateKeys []*packet.PrivateKey + + // e.PrivateKey is a signing key + if e.PrivateKey != nil { + privateKeys = append(privateKeys, e.PrivateKey) + } + + // e.Subkeys are encryption keys + for _, subkey := range e.Subkeys { + if subkey.PrivateKey != nil { + privateKeys = append(privateKeys, subkey.PrivateKey) + } + } + + for _, priv := range privateKeys { + if err := priv.Decrypt(passphraseBytes); err != nil { + return err + } + } + + return nil +} + func (c *Client) Unlock(auth *Auth, passphrase string) (openpgp.EntityList, error) { passphraseBytes := []byte(passphrase) if auth.keySalt != "" { @@ -218,24 +243,8 @@ func (c *Client) Unlock(auth *Auth, passphrase string) (openpgp.EntityList, erro } for _, e := range keyRing { - var privateKeys []*packet.PrivateKey - - // e.PrivateKey is a signing key - if e.PrivateKey != nil { - privateKeys = append(privateKeys, e.PrivateKey) - } - - // e.Subkeys are encryption keys - for _, subkey := range e.Subkeys { - if subkey.PrivateKey != nil { - privateKeys = append(privateKeys, subkey.PrivateKey) - } - } - - for _, priv := range privateKeys { - if err := priv.Decrypt(passphraseBytes); err != nil { - return nil, err - } + if err := unlockKey(e, passphraseBytes); err != nil { + return nil, err } } @@ -261,50 +270,37 @@ func (c *Client) Unlock(auth *Auth, passphrase string) (openpgp.EntityList, erro c.accessToken = string(accessTokenBytes) c.keyRing = keyRing - // Get additional private keys - u, err := c.GetCurrentUser() + // Unlock additional private keys + addrs, err := c.ListAddresses() if err != nil { return nil, err } - var keyRing2 openpgp.EntityList - for _, e := range u.Addresses { - for _, f := range e.Keys { - pkey, err := f.Entity() + + for _, addr := range addrs { + for _, key := range addr.Keys { + entity, err := key.Entity() if err != nil { return nil, err } - if pkey.PrimaryKey.KeyId == keyRing[0].PrimaryKey.KeyId { - continue - } - prKey, err := openpgp.ReadArmoredKeyRing(strings.NewReader(f.PrivateKey)) - // TODO Are these errors fatal? - if err != nil { - continue - } - if len(prKey) == 0 { - continue - } - keyRing2 = append(keyRing2, prKey[0]) - } - } - for _, e := range keyRing2 { - var privateKeys []*packet.PrivateKey - if e.PrivateKey != nil { - privateKeys = append(privateKeys, e.PrivateKey) - } - for _, subkey := range e.Subkeys { - if subkey.PrivateKey != nil { - privateKeys = append(privateKeys, subkey.PrivateKey) + found := false + for _, e := range keyRing { + if e.PrimaryKey.KeyIdString() == entity.PrimaryKey.KeyIdString() { + found = true + break + } + } + if found { + continue } - } - for _, priv := range privateKeys { - if err := priv.Decrypt(passphraseBytes); err != nil { - return nil, err + if err := unlockKey(entity, passphraseBytes); err != nil { + log.Printf("failed to unlock key %v: %v", entity.PrimaryKey.KeyIdString(), err) + continue } + + keyRing = append(keyRing, entity) } - keyRing = append(keyRing, e) } return keyRing, nil diff --git a/protonmail/keys.go b/protonmail/keys.go index ee5ceb7..3e72257 100644 --- a/protonmail/keys.go +++ b/protonmail/keys.go @@ -9,13 +9,21 @@ import ( "golang.org/x/crypto/openpgp" ) +type PrivateKeyFlags int + +const ( + PrivateKeyVerify PrivateKeyFlags = 1 + PrivateKeyEncrypt = 2 +) + type PrivateKey struct { ID string Version int - PublicKey string + Flags PrivateKeyFlags PrivateKey string Fingerprint string Activation interface{} // TODO + Primary int } func (priv *PrivateKey) Entity() (*openpgp.Entity, error) {