Refactor: Moved relevant parts of Key and KeyRing objs from go-pmapi
This commit is contained in:
parent
d1c55119c0
commit
b3e6e67cac
11 changed files with 1113 additions and 32 deletions
|
|
@ -5,12 +5,12 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
|
armorUtils "gitlab.com/ProtonMail/go-pm-crypto/armor"
|
||||||
|
"gitlab.com/ProtonMail/go-pm-crypto/internal"
|
||||||
|
"gitlab.com/ProtonMail/go-pm-crypto/models"
|
||||||
"golang.org/x/crypto/openpgp"
|
"golang.org/x/crypto/openpgp"
|
||||||
"golang.org/x/crypto/openpgp/armor"
|
"golang.org/x/crypto/openpgp/armor"
|
||||||
"golang.org/x/crypto/openpgp/packet"
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
armorUtils "proton/pmcrypto/armor"
|
|
||||||
"proton/pmcrypto/internal"
|
|
||||||
"proton/pmcrypto/models"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//EncryptAttachmentBinKey ...
|
//EncryptAttachmentBinKey ...
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gitlab.com/ProtonMail/go-pm-crypto/armor"
|
||||||
"golang.org/x/crypto/openpgp"
|
"golang.org/x/crypto/openpgp"
|
||||||
"golang.org/x/crypto/openpgp/packet"
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
"math/big"
|
"math/big"
|
||||||
"proton/pmcrypto/armor"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,14 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
armorUtils "gitlab.com/ProtonMail/go-pm-crypto/armor"
|
||||||
|
"gitlab.com/ProtonMail/go-pm-crypto/internal"
|
||||||
|
"gitlab.com/ProtonMail/go-pm-crypto/models"
|
||||||
"golang.org/x/crypto/openpgp"
|
"golang.org/x/crypto/openpgp"
|
||||||
"golang.org/x/crypto/openpgp/armor"
|
"golang.org/x/crypto/openpgp/armor"
|
||||||
errors2 "golang.org/x/crypto/openpgp/errors"
|
errors2 "golang.org/x/crypto/openpgp/errors"
|
||||||
"golang.org/x/crypto/openpgp/packet"
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
"math"
|
"math"
|
||||||
armorUtils "proton/pmcrypto/armor"
|
|
||||||
"proton/pmcrypto/internal"
|
|
||||||
"proton/pmcrypto/models"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// DecryptMessage decrypt encrypted message use private key (string )
|
// DecryptMessage decrypt encrypted message use private key (string )
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,21 @@
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"proton/pmmime"
|
|
||||||
"net/mail"
|
|
||||||
"strings"
|
|
||||||
"golang.org/x/crypto/openpgp/packet"
|
|
||||||
"net/textproto"
|
|
||||||
"io/ioutil"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"github.com/ProtonMail/go-pm-mime"
|
||||||
|
"gitlab.com/ProtonMail/go-pm-crypto/armor"
|
||||||
"golang.org/x/crypto/openpgp"
|
"golang.org/x/crypto/openpgp"
|
||||||
"proton/pmcrypto/armor"
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/mail"
|
||||||
|
"net/textproto"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ======================== Attachments Collector ==============
|
// ======================== Attachments Collector ==============
|
||||||
// Collect contents of all attachment parts and return
|
// Collect contents of all attachment parts and return
|
||||||
// them as a string
|
// them as a string
|
||||||
|
|
||||||
|
|
||||||
func (pm PmCrypto) parseMIME(mimeBody string, verifierKey []byte) (*pmmime.BodyCollector, int, []string, []string, error) {
|
func (pm PmCrypto) parseMIME(mimeBody string, verifierKey []byte) (*pmmime.BodyCollector, int, []string, []string, error) {
|
||||||
pubKey := bytes.NewReader(verifierKey)
|
pubKey := bytes.NewReader(verifierKey)
|
||||||
pubKeyEntries, err := openpgp.ReadKeyRing(pubKey)
|
pubKeyEntries, err := openpgp.ReadKeyRing(pubKey)
|
||||||
|
|
@ -34,7 +33,10 @@ func (pm PmCrypto) parseMIME(mimeBody string, verifierKey []byte) (*pmmime.BodyC
|
||||||
bodyCollector := pmmime.NewBodyCollector(printAccepter)
|
bodyCollector := pmmime.NewBodyCollector(printAccepter)
|
||||||
attachmentsCollector := pmmime.NewAttachmentsCollector(bodyCollector)
|
attachmentsCollector := pmmime.NewAttachmentsCollector(bodyCollector)
|
||||||
mimeVisitor := pmmime.NewMimeVisitor(attachmentsCollector)
|
mimeVisitor := pmmime.NewMimeVisitor(attachmentsCollector)
|
||||||
str, err := armor.ArmorKey(verifierKey)
|
// TODO: build was failing on this unused 'str' variable. This code looks like WIP
|
||||||
|
//str, err := armor.ArmorKey(verifierKey)
|
||||||
|
_, err = armor.ArmorKey(verifierKey)
|
||||||
|
|
||||||
signatureCollector := newSignatureCollector(mimeVisitor, pubKeyEntries, config)
|
signatureCollector := newSignatureCollector(mimeVisitor, pubKeyEntries, config)
|
||||||
err = pmmime.VisitAll(bytes.NewReader(mmBodyData), h, signatureCollector)
|
err = pmmime.VisitAll(bytes.NewReader(mmBodyData), h, signatureCollector)
|
||||||
|
|
||||||
|
|
@ -80,4 +82,4 @@ func (pm *PmCrypto) DecryptMIMEMessage(encryptedText string, verifierKey []byte,
|
||||||
} else {
|
} else {
|
||||||
callbacks.OnVerified(decsignverify.Verify)
|
callbacks.OnVerified(decsignverify.Verify)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"gitlab.com/ProtonMail/go-pm-crypto/armor"
|
||||||
|
"gitlab.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"
|
||||||
"proton/pmcrypto/armor"
|
|
||||||
"proton/pmcrypto/models"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//RandomToken ...
|
//RandomToken ...
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gitlab.com/ProtonMail/go-pm-crypto/internal"
|
||||||
"golang.org/x/crypto/openpgp"
|
"golang.org/x/crypto/openpgp"
|
||||||
"golang.org/x/crypto/openpgp/packet"
|
|
||||||
errors2 "golang.org/x/crypto/openpgp/errors"
|
errors2 "golang.org/x/crypto/openpgp/errors"
|
||||||
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
"io"
|
"io"
|
||||||
"proton/pmcrypto/internal"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignTextDetached sign detached text type
|
// SignTextDetached sign detached text type
|
||||||
|
|
@ -45,7 +45,7 @@ func (pm *PmCrypto) SignTextDetached(plainText string, privateKey string, passph
|
||||||
return "", errors.New("cannot sign message, signer key is not unlocked")
|
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()}
|
||||||
|
|
||||||
att := strings.NewReader(plainText)
|
att := strings.NewReader(plainText)
|
||||||
|
|
||||||
|
|
@ -90,7 +90,7 @@ func (pm *PmCrypto) SignTextDetachedBinKey(plainText string, privateKey []byte,
|
||||||
return "", errors.New("cannot sign message, singer key is not unlocked")
|
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()}
|
||||||
|
|
||||||
att := strings.NewReader(plainText)
|
att := strings.NewReader(plainText)
|
||||||
|
|
||||||
|
|
@ -131,7 +131,7 @@ func (pm *PmCrypto) SignBinDetached(plainData []byte, privateKey string, passphr
|
||||||
return "", errors.New("cannot sign message, singer key is not unlocked")
|
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()}
|
||||||
|
|
||||||
att := bytes.NewReader(plainData)
|
att := bytes.NewReader(plainData)
|
||||||
|
|
||||||
|
|
@ -172,7 +172,7 @@ func (pm *PmCrypto) SignBinDetachedBinKey(plainData []byte, privateKey []byte, p
|
||||||
return "", errors.New("cannot sign message, singer key is not unlocked")
|
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()}
|
||||||
|
|
||||||
att := bytes.NewReader(plainData)
|
att := bytes.NewReader(plainData)
|
||||||
|
|
||||||
|
|
@ -226,7 +226,7 @@ func verifySignature(pubKeyEntries openpgp.EntityList, origText *bytes.Reader, s
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
config.Time = func() time.Time {
|
config.Time = func() time.Time {
|
||||||
return time.Unix(verifyTime + internal.CreationTimeOffset, 0)
|
return time.Unix(verifyTime+internal.CreationTimeOffset, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
signatureReader := strings.NewReader(signature)
|
signatureReader := strings.NewReader(signature)
|
||||||
|
|
@ -261,7 +261,6 @@ func verifySignature(pubKeyEntries openpgp.EntityList, origText *bytes.Reader, s
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// VerifyBinSignDetached ...
|
// VerifyBinSignDetached ...
|
||||||
func (pm *PmCrypto) VerifyBinSignDetached(signature string, plainData []byte, publicKey string, verifyTime int64) (bool, error) {
|
func (pm *PmCrypto) VerifyBinSignDetached(signature string, plainData []byte, publicKey string, verifyTime int64) (bool, error) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"github.com/ProtonMail/go-pm-mime"
|
||||||
"golang.org/x/crypto/openpgp"
|
"golang.org/x/crypto/openpgp"
|
||||||
"golang.org/x/crypto/openpgp/packet"
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"mime"
|
"mime"
|
||||||
"mime/multipart"
|
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"proton/pmmime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SignatureCollector struct {
|
type SignatureCollector struct {
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,20 @@ func (pm *PmCrypto) UpdateTime(newTime int64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//GetTime get latest cached time
|
//GetTime get latest cached time
|
||||||
func (pm *PmCrypto) GetTime() int64 {
|
func (pm *PmCrypto) GetTimeUnix() int64 {
|
||||||
return pm.getNow().Unix()
|
return pm.getNow().Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//GetTime get latest cached time
|
||||||
|
func (pm *PmCrypto) GetTime() time.Time {
|
||||||
|
return pm.getNow()
|
||||||
|
}
|
||||||
|
|
||||||
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 monotome, it uses a monotime time clock in this case instead of the wall clock
|
// Sub is monotome, it uses a monotime time clock in this case instead of the wall clock
|
||||||
extrapolate := int64(pm.latestClientTime.Sub(time.Now()).Seconds())
|
extrapolate := int64(pm.latestClientTime.Sub(time.Now()).Seconds())
|
||||||
return time.Unix(pm.latestServerTime + extrapolate, 0)
|
return time.Unix(pm.latestServerTime+extrapolate, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Now()
|
return time.Now()
|
||||||
|
|
|
||||||
235
pmapi/key.go
Normal file
235
pmapi/key.go
Normal file
|
|
@ -0,0 +1,235 @@
|
||||||
|
package pmapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
// "net/http"
|
||||||
|
// "net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
//"gitlab.com/ProtonMail/go-pm-crypto/armor"
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A decrypted session key.
|
||||||
|
type SymmetricKey struct {
|
||||||
|
// The clear base64-encoded key.
|
||||||
|
Key string
|
||||||
|
// The algorithm used by this key.
|
||||||
|
Algo string
|
||||||
|
}
|
||||||
|
|
||||||
|
//18 with the 2 highest order bits set to 1
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get this's cipher function.
|
||||||
|
func (sk *SymmetricKey) cipherFunc() packet.CipherFunction {
|
||||||
|
cf, ok := symKeyAlgos[sk.Algo]
|
||||||
|
if ok {
|
||||||
|
return cf
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("pmapi: unsupported cipher function: " + sk.Algo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSymmetricKey(ek *packet.EncryptedKey) *SymmetricKey {
|
||||||
|
var algo string
|
||||||
|
for k, v := range symKeyAlgos {
|
||||||
|
if v == ek.CipherFunc {
|
||||||
|
algo = k
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if algo == "" {
|
||||||
|
panic(fmt.Sprintf("pmapi: unsupported cipher function: %v", ek.CipherFunc))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &SymmetricKey{
|
||||||
|
Key: base64.StdEncoding.EncodeToString(ek.Key),
|
||||||
|
Algo: algo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecryptAttKey(kr *KeyRing, keyPacket string) (key *SymmetricKey, err error) {
|
||||||
|
r := base64.NewDecoder(base64.StdEncoding, strings.NewReader(keyPacket))
|
||||||
|
packets := packet.NewReader(r)
|
||||||
|
|
||||||
|
var p packet.Packet
|
||||||
|
if p, err = packets.Next(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ek := p.(*packet.EncryptedKey)
|
||||||
|
|
||||||
|
var decryptErr error
|
||||||
|
for _, key := range kr.entities.DecryptionKeys() {
|
||||||
|
priv := key.PrivateKey
|
||||||
|
if priv.Encrypted {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if decryptErr = ek.Decrypt(priv, nil); decryptErr == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if decryptErr != nil {
|
||||||
|
err = fmt.Errorf("pmapi: cannot decrypt encrypted key packet: %v", decryptErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
key = newSymmetricKey(ek)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func SeparateKeyAndData(kr *KeyRing, r io.Reader) (key *SymmetricKey, symEncryptedData []byte, err error) {
|
||||||
|
packets := packet.NewReader(r)
|
||||||
|
|
||||||
|
// Save encrypted key and signature apart
|
||||||
|
var ek *packet.EncryptedKey
|
||||||
|
var decryptErr error
|
||||||
|
for {
|
||||||
|
var p packet.Packet
|
||||||
|
if p, err = packets.Next(); err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch p := p.(type) {
|
||||||
|
case *packet.EncryptedKey:
|
||||||
|
// We got an encrypted key. Try to decrypt it with each available key
|
||||||
|
if ek != nil && ek.Key != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ek = p
|
||||||
|
|
||||||
|
for _, key := range kr.entities.DecryptionKeys() {
|
||||||
|
priv := key.PrivateKey
|
||||||
|
if priv.Encrypted {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if decryptErr = ek.Decrypt(priv, nil); decryptErr == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *packet.SymmetricallyEncrypted:
|
||||||
|
var packetContents []byte
|
||||||
|
if packetContents, err = ioutil.ReadAll(p.Contents); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
encodedLength := encodedLength(len(packetContents) + 1)
|
||||||
|
|
||||||
|
symEncryptedData = append(symEncryptedData, byte(210))
|
||||||
|
symEncryptedData = append(symEncryptedData, encodedLength...)
|
||||||
|
symEncryptedData = append(symEncryptedData, byte(1))
|
||||||
|
symEncryptedData = append(symEncryptedData, packetContents...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if decryptErr != nil {
|
||||||
|
err = fmt.Errorf("pmapi: cannot decrypt encrypted key packet: %v", decryptErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ek == nil {
|
||||||
|
err = errors.New("pmapi: packets don't include an encrypted key packet")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ek.Key == nil {
|
||||||
|
err = errors.New("pmapi: could not find any key to decrypt key")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
key = newSymmetricKey(ek)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//encode length based on 4.2.2. in the RFC
|
||||||
|
func encodedLength(length int) (b []byte) {
|
||||||
|
if length < 192 {
|
||||||
|
b = append(b, byte(length))
|
||||||
|
} else if length < 8384 {
|
||||||
|
length = length - 192
|
||||||
|
b = append(b, 192+byte(length>>8))
|
||||||
|
b = append(b, byte(length))
|
||||||
|
} else {
|
||||||
|
b = append(b, byte(255))
|
||||||
|
b = append(b, byte(length>>24))
|
||||||
|
b = append(b, byte(length>>16))
|
||||||
|
b = append(b, byte(length>>8))
|
||||||
|
b = append(b, byte(length))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetKey encrypts the provided key.
|
||||||
|
func SetKey(kr *KeyRing, symKey *SymmetricKey) (packets string, err error) {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
w := base64.NewEncoder(base64.StdEncoding, b)
|
||||||
|
|
||||||
|
cf := symKey.cipherFunc()
|
||||||
|
|
||||||
|
k, err := base64.StdEncoding.DecodeString(symKey.Key)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("pmapi: cannot set key: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(kr.entities) == 0 {
|
||||||
|
err = fmt.Errorf("pmapi: cannot set key: key ring is empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var pub *packet.PublicKey
|
||||||
|
for _, e := range kr.entities {
|
||||||
|
for _, subKey := range e.Subkeys {
|
||||||
|
if !subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications {
|
||||||
|
pub = subKey.PublicKey
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pub == nil && len(e.Identities) > 0 {
|
||||||
|
var i *openpgp.Identity
|
||||||
|
for _, i = range e.Identities {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptStorage || i.SelfSignature.FlagEncryptCommunications {
|
||||||
|
pub = e.PrimaryKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pub != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pub == nil {
|
||||||
|
err = fmt.Errorf("pmapi: cannot set key: no public key available")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = packet.SerializeEncryptedKey(w, pub, cf, k, nil); err != nil {
|
||||||
|
err = fmt.Errorf("pmapi: cannot set key: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = w.Close(); err != nil {
|
||||||
|
err = fmt.Errorf("pmapi: cannot set key: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
packets = b.String()
|
||||||
|
return
|
||||||
|
}
|
||||||
559
pmapi/keyring.go
Normal file
559
pmapi/keyring.go
Normal file
|
|
@ -0,0 +1,559 @@
|
||||||
|
package pmapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rsa"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
"golang.org/x/crypto/openpgp/armor"
|
||||||
|
pgperrors "golang.org/x/crypto/openpgp/errors"
|
||||||
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
|
|
||||||
|
"gitlab.com/ProtonMail/go-pm-crypto/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Armored type for PGP encrypted messages.
|
||||||
|
const pgpMessageType = "PGP MESSAGE"
|
||||||
|
|
||||||
|
// A keypair contains a private key and a public key.
|
||||||
|
type pmKeyObject struct {
|
||||||
|
ID string
|
||||||
|
Version int
|
||||||
|
Flags int
|
||||||
|
Fingerprint string
|
||||||
|
PublicKey string `json:",omitempty"`
|
||||||
|
PrivateKey string
|
||||||
|
//Activation string // Undocumented
|
||||||
|
Primary int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ko *pmKeyObject) PrivateKeyReader() io.Reader {
|
||||||
|
return strings.NewReader(ko.PrivateKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identity contains the name and the email of a key holder.
|
||||||
|
type Identity struct {
|
||||||
|
Name string
|
||||||
|
Email string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature is be used to check a signature. Because the signature is checked
|
||||||
|
// when the reader is consumed, Signature must only be used after EOF has been
|
||||||
|
// seen. A signature is only valid if s.Err() returns nil, otherwise the
|
||||||
|
// sender's identity cannot be trusted.
|
||||||
|
type Signature struct {
|
||||||
|
md *openpgp.MessageDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
var errKeyringNotUnlocked = errors.New("pmapi: cannot sign message, key ring is not unlocked")
|
||||||
|
|
||||||
|
// Err returns a non-nil error if the signature is invalid.
|
||||||
|
func (s *Signature) Err() error {
|
||||||
|
return s.md.SignatureError
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyRing returns the key ring that was used to produce the signature, if
|
||||||
|
// available.
|
||||||
|
func (s *Signature) KeyRing() *KeyRing {
|
||||||
|
if s.md.SignedBy == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &KeyRing{
|
||||||
|
entities: openpgp.EntityList{s.md.SignedBy.Entity},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBy returns true if the signature has been created by kr's owner.
|
||||||
|
func (s *Signature) IsBy(kr *KeyRing) bool {
|
||||||
|
// Use fingerprint if possible
|
||||||
|
if s.md.SignedBy != nil {
|
||||||
|
for _, e := range kr.entities {
|
||||||
|
if e.PrimaryKey.Fingerprint == s.md.SignedBy.PublicKey.Fingerprint {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range kr.entities {
|
||||||
|
if e.PrimaryKey.KeyId == s.md.SignedByKeyId {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// A keyring contains multiple private and public keys.
|
||||||
|
type KeyRing struct {
|
||||||
|
// PGP entities in this keyring.
|
||||||
|
entities openpgp.EntityList
|
||||||
|
pmCrypto *crypto.PmCrypto
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kr *KeyRing) GetEntities() openpgp.EntityList {
|
||||||
|
return kr.entities
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt encrypts data to this keyring's owner. If sign is not nil, it also
|
||||||
|
// signs data with it. sign must be unlock to be able to sign data, if it's not
|
||||||
|
// the case an error will be returned.
|
||||||
|
func (kr *KeyRing) Encrypt(w io.Writer, sign *KeyRing, filename string, canonicalizeText bool) (io.WriteCloser, error) {
|
||||||
|
// The API returns keys sorted by descending priority
|
||||||
|
// Only encrypt to the first one
|
||||||
|
var encryptEntities []*openpgp.Entity
|
||||||
|
for _, e := range kr.entities {
|
||||||
|
encryptEntities = append(encryptEntities, e)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
var signEntity *openpgp.Entity
|
||||||
|
if sign != nil {
|
||||||
|
// To sign a message, the private key must be decrypted
|
||||||
|
for _, e := range sign.entities {
|
||||||
|
// Entity.PrivateKey must be a signing key
|
||||||
|
if e.PrivateKey != nil && !e.PrivateKey.Encrypted {
|
||||||
|
signEntity = e
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if signEntity == nil {
|
||||||
|
return nil, errKeyringNotUnlocked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &packet.Config{DefaultCipher: packet.CipherAES256,
|
||||||
|
Time: func() time.Time {
|
||||||
|
return kr.pmCrypto.GetTime()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
hints := &openpgp.FileHints{
|
||||||
|
IsBinary: !canonicalizeText,
|
||||||
|
FileName: filename,
|
||||||
|
}
|
||||||
|
if canonicalizeText {
|
||||||
|
return openpgp.EncryptText(w, encryptEntities, signEntity, hints, config)
|
||||||
|
} else {
|
||||||
|
return openpgp.Encrypt(w, encryptEntities, signEntity, hints, config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kr *KeyRing) EncryptSymmetric(textToEncrypt string, canonicalizeText bool) (key *SymmetricKey, symEncryptedData []byte, err error) {
|
||||||
|
|
||||||
|
var encryptedWriter io.WriteCloser
|
||||||
|
buffer := &bytes.Buffer{}
|
||||||
|
|
||||||
|
if encryptedWriter, err = kr.Encrypt(buffer, kr, "msg.txt", canonicalizeText); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = io.Copy(encryptedWriter, bytes.NewBufferString(textToEncrypt)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
encryptedWriter.Close()
|
||||||
|
|
||||||
|
if key, symEncryptedData, err = SeparateKeyAndData(kr, buffer); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// An io.WriteCloser that both encrypts and armors data.
|
||||||
|
type armorEncryptWriter struct {
|
||||||
|
aw io.WriteCloser // Armored writer
|
||||||
|
ew io.WriteCloser // Encrypted writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *armorEncryptWriter) Write(b []byte) (n int, err error) {
|
||||||
|
return w.ew.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *armorEncryptWriter) Close() (err error) {
|
||||||
|
if err = w.ew.Close(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = w.aw.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncryptArmored encrypts and armors data to the keyring's owner.
|
||||||
|
func (kr *KeyRing) EncryptArmored(w io.Writer, sign *KeyRing) (wc io.WriteCloser, err error) {
|
||||||
|
aw, err := armor.Encode(w, pgpMessageType, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ew, err := kr.Encrypt(aw, sign, "", false)
|
||||||
|
if err != nil {
|
||||||
|
aw.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wc = &armorEncryptWriter{aw: aw, ew: ew}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncryptString encrypts and armors a string to the keyring's owner.
|
||||||
|
func (kr *KeyRing) EncryptString(s string, sign *KeyRing) (encrypted string, err error) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
w, err := kr.EncryptArmored(&b, sign)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = w.Write([]byte(s)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = w.Close(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypted = b.String()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kr *KeyRing) SignString(message string, canonicalizeText bool) (signed string, err error) {
|
||||||
|
|
||||||
|
var sig bytes.Buffer
|
||||||
|
err = kr.DetachedSign(&sig, strings.NewReader(message), canonicalizeText, true)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
} else {
|
||||||
|
return sig.String(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kr *KeyRing) DetachedSign(w io.Writer, toSign io.Reader, canonicalizeText bool, armored bool) (err error) {
|
||||||
|
|
||||||
|
var signEntity *openpgp.Entity
|
||||||
|
for _, e := range kr.entities {
|
||||||
|
if e.PrivateKey != nil && !e.PrivateKey.Encrypted {
|
||||||
|
signEntity = e
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if signEntity == nil {
|
||||||
|
return errKeyringNotUnlocked
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &packet.Config{DefaultCipher: packet.CipherAES256,
|
||||||
|
Time: func() time.Time {
|
||||||
|
return kr.pmCrypto.GetTime()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if canonicalizeText {
|
||||||
|
err = openpgp.ArmoredDetachSignText(w, signEntity, toSign, config)
|
||||||
|
} else {
|
||||||
|
if armored {
|
||||||
|
err = openpgp.ArmoredDetachSign(w, signEntity, toSign, config)
|
||||||
|
} else {
|
||||||
|
err = openpgp.DetachSign(w, signEntity, toSign, config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// May return errors.ErrSignatureExpired (defined in golang.org/x/crypto/openpgp/errors)
|
||||||
|
// In this case signature has been verified successfuly, but it is either expired or
|
||||||
|
// in the future.
|
||||||
|
func (kr *KeyRing) VerifyString(message, signature string, sign *KeyRing) (err error) {
|
||||||
|
|
||||||
|
messageReader := strings.NewReader(message)
|
||||||
|
signatureReader := strings.NewReader(signature)
|
||||||
|
|
||||||
|
err = nil
|
||||||
|
if sign != nil {
|
||||||
|
for _, e := range sign.entities {
|
||||||
|
if e.PrivateKey != nil && !e.PrivateKey.Encrypted {
|
||||||
|
_, err = openpgp.CheckArmoredDetachedSignature(kr.entities, messageReader, signatureReader, nil)
|
||||||
|
if err == nil || err == pgperrors.ErrSignatureExpired {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return errKeyringNotUnlocked
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock unlocks as many keys as possible with the following password. Note
|
||||||
|
// that keyrings can contain keys locked with different passwords, and thus
|
||||||
|
// err == nil does not mean that all keys have been successfully decrypted.
|
||||||
|
// If err != nil, the password is wrong for every key, and err is the last error
|
||||||
|
// encountered.
|
||||||
|
func (kr *KeyRing) Unlock(passphrase []byte) error {
|
||||||
|
// Build a list of keys to decrypt
|
||||||
|
var keys []*packet.PrivateKey
|
||||||
|
for _, e := range kr.entities {
|
||||||
|
// Entity.PrivateKey must be a signing key
|
||||||
|
if e.PrivateKey != nil {
|
||||||
|
keys = append(keys, e.PrivateKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
keys = append(keys, subKey.PrivateKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return errors.New("pmapi: cannot unlock key ring, no private key available")
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var n int
|
||||||
|
for _, key := range keys {
|
||||||
|
if !key.Encrypted {
|
||||||
|
continue // Key already decrypted
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = key.Decrypt(passphrase); err == nil {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts a message sent to the keypair's owner. If the message is not
|
||||||
|
// signed, signed will be nil.
|
||||||
|
// 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) Decrypt(r io.Reader) (decrypted io.Reader, signed *Signature, err error) {
|
||||||
|
md, err := openpgp.ReadMessage(r, kr.entities, nil, nil)
|
||||||
|
if err != nil && err != pgperrors.ErrSignatureExpired {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypted = md.UnverifiedBody
|
||||||
|
if md.IsSigned {
|
||||||
|
signed = &Signature{md}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecryptArmored decrypts an armored message sent to the keypair's owner.
|
||||||
|
// 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) DecryptArmored(r io.Reader) (decrypted io.Reader, signed *Signature, err error) {
|
||||||
|
block, err := armor.Decode(r)
|
||||||
|
if err != nil && err != pgperrors.ErrSignatureExpired {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if block.Type != pgpMessageType {
|
||||||
|
err = errors.New("pmapi: not an armored PGP message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return kr.Decrypt(block.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecryptString decrypts an armored string sent to the keypair's owner.
|
||||||
|
// 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) DecryptString(encrypted string) (s string, signed *Signature, err error) {
|
||||||
|
r, signed, err := kr.DecryptArmored(strings.NewReader(encrypted))
|
||||||
|
if err != nil && err != pgperrors.ErrSignatureExpired {
|
||||||
|
return encrypted, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil && err != pgperrors.ErrSignatureExpired {
|
||||||
|
return encrypted, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s = string(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteArmoredPublicKey outputs armored public keys from the keyring to w.
|
||||||
|
func (kr *KeyRing) WriteArmoredPublicKey(w io.Writer) (err error) {
|
||||||
|
aw, err := armor.Encode(w, openpgp.PublicKeyType, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range kr.entities {
|
||||||
|
if err = e.Serialize(aw); err != nil {
|
||||||
|
aw.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = aw.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArmoredPublicKeyString returns the armored public keys from this keyring.
|
||||||
|
func (kr *KeyRing) ArmoredPublicKeyString() (s string, err error) {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
if err = kr.WriteArmoredPublicKey(b); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s = b.String()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// readFrom reads unarmored and armored keys from r and adds them to the keyring.
|
||||||
|
func (kr *KeyRing) readFrom(r io.Reader, armored bool) error {
|
||||||
|
var err error
|
||||||
|
var entities openpgp.EntityList
|
||||||
|
if armored {
|
||||||
|
entities, err = openpgp.ReadArmoredKeyRing(r)
|
||||||
|
} else {
|
||||||
|
entities, err = openpgp.ReadKeyRing(r)
|
||||||
|
}
|
||||||
|
for _, entity := range entities {
|
||||||
|
if entity.PrivateKey != nil {
|
||||||
|
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().(*rsa.PublicKey))
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
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().(*rsa.PublicKey))
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
subkey.PublicKey = packet.NewECDSAPublicKey(time.Now(), subkey.PrivateKey.PrivateKey.(*ecdsa.PrivateKey).Public().(*ecdsa.PublicKey))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(entities) == 0 {
|
||||||
|
return errors.New("pmapi: key ring doesn't contain any key")
|
||||||
|
}
|
||||||
|
|
||||||
|
kr.entities = append(kr.entities, entities...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements encoding/json.Unmarshaler.
|
||||||
|
func (kr *KeyRing) UnmarshalJSON(b []byte) (err error) {
|
||||||
|
kr.entities = nil
|
||||||
|
|
||||||
|
keyObjs := []pmKeyObject{}
|
||||||
|
if err = json.Unmarshal(b, &keyObjs); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(keyObjs) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ko := range keyObjs {
|
||||||
|
kr.readFrom(ko.PrivateKeyReader(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identities returns the list of identities associated with this key ring.
|
||||||
|
func (kr *KeyRing) Identities() []*Identity {
|
||||||
|
var identities []*Identity
|
||||||
|
for _, e := range kr.entities {
|
||||||
|
for _, id := range e.Identities {
|
||||||
|
identities = append(identities, &Identity{
|
||||||
|
Name: id.UserId.Name,
|
||||||
|
Email: id.UserId.Email,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return identities
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kr *KeyRing) KeyIds() []uint64 {
|
||||||
|
var res []uint64
|
||||||
|
for _, e := range kr.entities {
|
||||||
|
res = append(res, e.PrimaryKey.KeyId)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadArmoredKeyRing reads an armored keyring.
|
||||||
|
func ReadArmoredKeyRing(r io.Reader) (kr *KeyRing, err error) {
|
||||||
|
kr = &KeyRing{}
|
||||||
|
err = kr.readFrom(r, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadArmoredKeyRing reads an armored keyring.
|
||||||
|
func ReadKeyRing(r io.Reader) (kr *KeyRing, err error) {
|
||||||
|
kr = &KeyRing{}
|
||||||
|
err = kr.readFrom(r, false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterExpiredKeys(contactKeys []*KeyRing) (filteredKeys []*KeyRing, err error) {
|
||||||
|
now := time.Now()
|
||||||
|
hasExpiredEntity := false
|
||||||
|
filteredKeys = make([]*KeyRing, 0, 0)
|
||||||
|
|
||||||
|
for _, contactKeyRing := range contactKeys {
|
||||||
|
keyRingHasUnexpiredEntity := false
|
||||||
|
keyRingHasTotallyExpiredEntity := false
|
||||||
|
for _, entity := range contactKeyRing.GetEntities() {
|
||||||
|
hasExpired := false
|
||||||
|
hasUnexpired := false
|
||||||
|
for _, subkey := range entity.Subkeys {
|
||||||
|
if subkey.Sig.KeyExpired(now) {
|
||||||
|
hasExpired = true
|
||||||
|
} else {
|
||||||
|
hasUnexpired = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hasExpired && !hasUnexpired {
|
||||||
|
keyRingHasTotallyExpiredEntity = true
|
||||||
|
} else if hasUnexpired {
|
||||||
|
keyRingHasUnexpiredEntity = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if keyRingHasUnexpiredEntity {
|
||||||
|
filteredKeys = append(filteredKeys, contactKeyRing)
|
||||||
|
} else if keyRingHasTotallyExpiredEntity {
|
||||||
|
hasExpiredEntity = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(filteredKeys) == 0 && hasExpiredEntity {
|
||||||
|
return filteredKeys, errors.New("all contacts keys are expired")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
283
pmapi/keyring_test.go
Normal file
283
pmapi/keyring_test.go
Normal file
|
|
@ -0,0 +1,283 @@
|
||||||
|
package pmapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"golang.org/x/crypto/openpgp/armor"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const testPrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
Version: OpenPGP.js v0.7.1
|
||||||
|
Comment: http://openpgpjs.org
|
||||||
|
|
||||||
|
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
|
||||||
|
qqGRQm3MxoTdgQUShAwbNwNNQR9cInfMnA==
|
||||||
|
=2wIY
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----
|
||||||
|
`
|
||||||
|
|
||||||
|
const testPrivateKeyLegacy = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
Version: OpenPGP.js v0.9.0
|
||||||
|
Comment: http://openpgpjs.org
|
||||||
|
|
||||||
|
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
|
||||||
|
PPekG4ttm6z3/BqqVplanIRSXlsqyp6J8A==
|
||||||
|
=Pyb1
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----
|
||||||
|
`
|
||||||
|
|
||||||
|
const testPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Version: OpenPGP.js v0.7.1
|
||||||
|
Comment: http://openpgpjs.org
|
||||||
|
|
||||||
|
xsBNBFRJbc0BCAC0mMLZPDBbtSCWvxwmOfXfJkE2+ssM3ux21LhD/bPiWefE
|
||||||
|
WSHlCjJ8PqPHy7snSiUuxuj3f9AvXPvg+mjGLBwu1/QsnSP24sl3qD2onl39
|
||||||
|
vPiLJXUqZs20ZRgnvX70gjkgEzMFBxINiy2MTIG+4RU8QA7y8KzWev0btqKi
|
||||||
|
MeVa+GLEHhgZ2KPOn4Jv1q4bI9hV0C9NUe2tTXS6/Vv3vbCY7lRR0kbJ65T5
|
||||||
|
c8CmpqJuASIJNrSXM/Q3NnnsY4kBYH0s5d2FgbASQvzrjuC2rngUg0EoPsrb
|
||||||
|
DEVRA2/BCJonw7aASiNCrSP92lkZdtYlax/pcoE/mQ4WSwySFmcFT7yFABEB
|
||||||
|
AAHNBlVzZXJJRMLAcgQQAQgAJgUCVEltzwYLCQgHAwIJED62JZ7fId8kBBUI
|
||||||
|
AgoDFgIBAhsDAh4BAAD0nQf9EtH9TC0JqSs8q194Zo244jjlJFM3EzxOSULq
|
||||||
|
0zbywlLORfyoo/O8jU/HIuGz+LT98JDtnltTqfjWgu6pS3ZL2/L4AGUKEoB7
|
||||||
|
OI6oIdRwzMc61sqI+Qpbzxo7rzufH4CiXZc6cxORUgL550xSCcqnq0q1mds7
|
||||||
|
h5roKDzxMW6WLiEsc1dN8IQKzC7Ec5wA7U4oNGsJ3TyI8jkIs0IhXrRCd26K
|
||||||
|
0TW8Xp6GCsfblWXosR13y89WVNgC+xrrJKTZEisc0tRlneIgjcwEUvwfIg2n
|
||||||
|
9cDUFA/5BsfzTW5IurxqDEziIVP0L44PXjtJrBQaGMPlEbtP5i2oi3OADVX2
|
||||||
|
XbvsRc7ATQRUSW3PAQgAkPnu5fps5zhOB/e618v/iF3KiogxUeRhA68TbvA+
|
||||||
|
xnFfTxCx2Vo14aOL0CnaJ8gO5yRSqfomL2O1kMq07N1MGbqucbmc+aSfoElc
|
||||||
|
+Gd5xBE/w3RcEhKcAaYTi35vG22zlZup4x3ElioyIarOssFEkQgNNyDf5AXZ
|
||||||
|
jdHLA6qVxeqAb/Ff74+y9HUmLPSsRU9NwFzvK3Jv8C/ubHVLzTYdFgYkc4W1
|
||||||
|
Uug9Ou08K+/4NEMrwnPFBbZdJAuUjQz2zW2ZiEKiBggiorH2o5N3mYUnWEmU
|
||||||
|
vqL3EOS8TbWo8UBIW3DDm2JiZR8VrEgvBtc9mVDUj/x+5pR07Fy1D6DjRmAc
|
||||||
|
9wARAQABwsBfBBgBCAATBQJUSW3SCRA+tiWe3yHfJAIbDAAA/iwH/ik9RKZM
|
||||||
|
B9Ir0x5mGpKPuqhugwrc3d04m1sOdXJm2NtD4ddzSEvzHwaPNvEvUl5v7FVM
|
||||||
|
zf6+6mYGWHyNP4+e7RtwYLlRpud6smuGyDSsotUYyumiqP6680ZIeWVQ+a1T
|
||||||
|
ThNs878mAJy1FhvQFdTmA8XIC616hDFpamQKPlpoO1a0wZnQhrPwT77HDYEE
|
||||||
|
a+hqY4Jr/a7ui40S+7xYRHKL/7ZAS4/grWllhU3dbNrwSzrOKwrA/U0/9t73
|
||||||
|
8Ap6JL71YymDeaL4sutcoaahda1pTrMWePtrCltz6uySwbZs7GXoEzjX3EAH
|
||||||
|
+6qhkUJtzMaE3YEFEoQMGzcDTUEfXCJ3zJw=
|
||||||
|
=yT9U
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
`
|
||||||
|
|
||||||
|
const testMailboxPassword = "apple"
|
||||||
|
const testMailboxPasswordLegacy = "123"
|
||||||
|
|
||||||
|
const testToken = "d79ca194a22810a5363eeddfdef7dfbc327c6229"
|
||||||
|
|
||||||
|
const testEncryptedToken = `-----BEGIN PGP MESSAGE-----
|
||||||
|
Version: OpenPGP.js v1.2.0
|
||||||
|
Comment: http://openpgpjs.org
|
||||||
|
|
||||||
|
wcBMA0fcZ7XLgmf2AQf/RxDfA7g85KzH4371D/jx6deJIXPOWAqgTlGQMsTt
|
||||||
|
yg4ny3phSC2An/bUXNEBm8UMXqqtS7O+S8n1GjkDrCOkxyC+HugOFQwtybzI
|
||||||
|
eRX0X0qqvR6ry940SNGjPfJJ4Z0FYSLJtT8YxqO38t38WAYV1j9mBBVPMPJF
|
||||||
|
r7cQXxEcQAd6NZWF1Cf5Ajuum/zFjbA10Ksbi1tC4fsdtHcS94h1GCfsdNQi
|
||||||
|
xxbAuoyNYX2wsc6WX8IcmDNn564ZoHfvf2tX4Csf+2czByyOPtfyCn1aee51
|
||||||
|
I40/I+65w8NfYEfzu7pbUcdo041Xg3lOhDNcuX/zANNw6zEWbE+12G5KVvwC
|
||||||
|
NNJgARWnwnOKtov2d73wGqNawn21SzA+zEd2mAPv1LPPIupW+0xOUSp5muov
|
||||||
|
aLEjcIuZeu+vyhXGZxIgoY4Bw8XCO9uWKZuzmqp+AOIP+kSi5aWnOaDFIOq0
|
||||||
|
B3KtZ33bMZeX
|
||||||
|
=mig5
|
||||||
|
-----END PGP MESSAGE-----
|
||||||
|
`
|
||||||
|
|
||||||
|
var (
|
||||||
|
testPrivateKeyRing *KeyRing
|
||||||
|
testPublicKeyRing *KeyRing
|
||||||
|
)
|
||||||
|
|
||||||
|
var testIdentity = &Identity{
|
||||||
|
Name: "UserID",
|
||||||
|
Email: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
if testPrivateKeyRing, err = ReadArmoredKeyRing(strings.NewReader(testPrivateKey)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if testPublicKeyRing, err = ReadArmoredKeyRing(strings.NewReader(testPublicKey)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := testPrivateKeyRing.Unlock([]byte(testMailboxPassword)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyRing_Decrypt(t *testing.T) {
|
||||||
|
s, _, err := testPrivateKeyRing.DecryptString(testEncryptedToken)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot decrypt token:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s != testToken {
|
||||||
|
t.Fatalf("Invalid decrypted token: want %v but got %v", testToken, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyRing_Encrypt(t *testing.T) {
|
||||||
|
encrypted, err := testPublicKeyRing.EncryptString(testToken, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot encrypt token:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can't just check if encrypted == testEncryptedToken
|
||||||
|
// Decrypt instead
|
||||||
|
s, _, err := testPrivateKeyRing.DecryptString(encrypted)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot decrypt token:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s != testToken {
|
||||||
|
t.Fatalf("Invalid decrypted token: want %v but got %v", testToken, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyRing_ArmoredPublicKeyString(t *testing.T) {
|
||||||
|
s, err := testPrivateKeyRing.ArmoredPublicKeyString()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected no error while getting armored public key, got:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode armored keys
|
||||||
|
block, err := armor.Decode(strings.NewReader(s))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected no error while decoding armored public key, got:", err)
|
||||||
|
}
|
||||||
|
expected, err := armor.Decode(strings.NewReader(testPublicKey))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected no error while decoding expected armored public key, got:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected.Type != block.Type {
|
||||||
|
t.Fatalf("Invalid public key block type: expected %v, got %v", expected.Type, block.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := ioutil.ReadAll(block.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected no error while reading armored public key body, got:", err)
|
||||||
|
}
|
||||||
|
eb, err := ioutil.ReadAll(expected.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected no error while reading expected armored public key body, got:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(eb, b) != 0 {
|
||||||
|
t.Fatal("Invalid public key body: expected %v, got %v", eb, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue