Issue #3: mobile client fallback version

This commit is contained in:
Jakub Lehotsky 2018-11-05 22:55:45 +01:00
parent 0bf075d49d
commit fdd1a727cb
15 changed files with 367 additions and 1321 deletions

View file

@ -1,13 +1,12 @@
// This package contains a set of helper methods for armoring and unarmoring
package armor
import (
"bytes"
"errors"
"github.com/ProtonMail/go-pm-crypto/internal"
"github.com/ProtonMail/go-pm-crypto/models"
"golang.org/x/crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/clearsign"
"golang.org/x/crypto/openpgp/packet"
"io"
"io/ioutil"
)
@ -17,10 +16,17 @@ func ArmorKey(input []byte) (string, error) {
return ArmorWithType(input, PUBLIC_KEY_HEADER)
}
// ArmorWithTypeBuffered take input from io.Writer and returns io.WriteCloser which can be read for armored code
func ArmorWithTypeBuffered(w io.Writer, armorType string) (io.WriteCloser, error) {
return armor.Encode(w, armorType, nil)
}
// ArmorWithType make bytes input to armor format
func ArmorWithType(input []byte, armorType string) (string, error) {
var b bytes.Buffer
w, err := armor.Encode(&b, armorType, internal.ArmorHeaders)
if err != nil {
return "", err
}
@ -41,7 +47,7 @@ func Unarmor(input string) ([]byte, error) {
return ioutil.ReadAll(b.Body)
}
//ReadClearSignedMessage read clear message from a clearsign package
//ReadClearSignedMessage read clear message from a clearsign package (package containing cleartext and signature)
func ReadClearSignedMessage(signedMessage string) (string, error) {
modulusBlock, rest := clearsign.Decode([]byte(signedMessage))
if len(rest) != 0 {
@ -49,81 +55,3 @@ func ReadClearSignedMessage(signedMessage string) (string, error) {
}
return string(modulusBlock.Bytes), nil
}
//SeparateKeyAndData ...
func SplitArmor(encrypted string) (*models.EncryptedSplit, error) {
var err error
encryptedRaw, err := Unarmor(encrypted)
if err != nil {
return nil, err
}
encryptedReader := bytes.NewReader(encryptedRaw)
//kr *KeyRing, r io.Reader) (key *SymmetricKey, symEncryptedData []byte,
packets := packet.NewReader(encryptedReader)
outSplit := &models.EncryptedSplit{}
// 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
break
case *packet.SymmetricallyEncrypted:
var packetContents []byte
if packetContents, err = ioutil.ReadAll(p.Contents); err != nil {
return nil, err
}
encodedLength := encodedLength(len(packetContents) + 1)
var symEncryptedData []byte
symEncryptedData = append(symEncryptedData, byte(210))
symEncryptedData = append(symEncryptedData, encodedLength...)
symEncryptedData = append(symEncryptedData, byte(1))
symEncryptedData = append(symEncryptedData, packetContents...)
outSplit.DataPacket = symEncryptedData
break
}
}
var buf bytes.Buffer
ek.Serialize(&buf)
outSplit.KeyPacket = buf.Bytes()
return outSplit, err
}
//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
}

View file

@ -7,7 +7,7 @@ import (
const (
ARMOR_HEADER_VERSION = internal.ARMOR_HEADER_VERSION
ARMOR_HEADER_COMMENT = internal.ARMOR_HEADER_COMMENT
MESSAGE_HEADER string = "PGP MESSAGE"
PGP_MESSAGE_HEADER string = "PGP MESSAGE"
PUBLIC_KEY_HEADER string = "PGP PUBLIC KEY BLOCK"
PRIVATE_KEY_HEADER string = "PGP PRIVATE KEY BLOCK"
)

View file

@ -13,11 +13,11 @@ import (
"golang.org/x/crypto/openpgp/packet"
)
//EncryptAttachmentBinKey ...
// Encrypt attachment. Takes input data and key data in binary form
func (pm *PmCrypto) EncryptAttachmentBinKey(plainData []byte, fileName string, publicKey []byte) (*models.EncryptedSplit, error) {
var outBuf bytes.Buffer
w, err := armor.Encode(&outBuf, armorUtils.MESSAGE_HEADER, internal.ArmorHeaders)
w, err := armor.Encode(&outBuf, armorUtils.PGP_MESSAGE_HEADER, internal.ArmorHeaders)
if err != nil {
return nil, err
}
@ -42,7 +42,7 @@ func (pm *PmCrypto) EncryptAttachmentBinKey(plainData []byte, fileName string, p
ew.Close()
w.Close()
split, err := armorUtils.SplitArmor(outBuf.String())
split, err := SplitArmor(outBuf.String())
if err != nil {
return nil, err
}
@ -50,7 +50,23 @@ func (pm *PmCrypto) EncryptAttachmentBinKey(plainData []byte, fileName string, p
return split, nil
}
//EncryptAttachment ...
// Helper method. Splits armored pgp session into key and packet data
func SplitArmor(encrypted string) (*models.EncryptedSplit, error) {
var err error
encryptedRaw, err := armorUtils.Unarmor(encrypted)
if err != nil {
return nil, err
}
encryptedReader := bytes.NewReader(encryptedRaw)
return SeparateKeyAndData(nil, encryptedReader)
}
// Encrypt attachment. Takes input data in binary form and key in as a string
func (pm *PmCrypto) EncryptAttachment(plainData []byte, fileName string, publicKey string) (*models.EncryptedSplit, error) {
rawPubKey, err := armorUtils.Unarmor(publicKey)
if err != nil {
@ -59,10 +75,7 @@ func (pm *PmCrypto) EncryptAttachment(plainData []byte, fileName string, publicK
return pm.EncryptAttachmentBinKey(plainData, fileName, rawPubKey)
}
//DecryptAttachmentBinKey ...
//keyPacket
//dataPacket
//privateKeys could be mutiple private keys
// Decrypt attachment. Takes input data and key data in binary form. privateKeys can contains more keys. passphrase is used to unlock keys
func (pm *PmCrypto) DecryptAttachmentBinKey(keyPacket []byte, dataPacket []byte, privateKeys []byte, passphrase string) ([]byte, error) {
privKeyRaw := bytes.NewReader(privateKeys)
privKeyEntries, err := openpgp.ReadKeyRing(privKeyRaw)
@ -105,7 +118,7 @@ func (pm *PmCrypto) DecryptAttachmentBinKey(keyPacket []byte, dataPacket []byte,
return b, nil
}
//DecryptAttachment ...
// Decrypt attachment. Takes input data and key data in binary form and key as an armored string. passphrase is used to unlock keys
func (pm *PmCrypto) DecryptAttachment(keyPacket []byte, dataPacket []byte, privateKey string, passphrase string) ([]byte, error) {
rawPrivKey, err := armorUtils.Unarmor(privateKey)
if err != nil {
@ -114,11 +127,11 @@ func (pm *PmCrypto) DecryptAttachment(keyPacket []byte, dataPacket []byte, priva
return pm.DecryptAttachmentBinKey(keyPacket, dataPacket, rawPrivKey, passphrase)
}
//EncryptAttachmentWithPassword ...
//Encrypt attachment. Use symmetrical cipher with key in password input string
func (pm *PmCrypto) EncryptAttachmentWithPassword(plainData []byte, password string) (string, error) {
var outBuf bytes.Buffer
w, err := armor.Encode(&outBuf, armorUtils.MESSAGE_HEADER, internal.ArmorHeaders)
w, err := armor.Encode(&outBuf, armorUtils.PGP_MESSAGE_HEADER, internal.ArmorHeaders)
if err != nil {
return "", err
}
@ -143,7 +156,7 @@ func (pm *PmCrypto) EncryptAttachmentWithPassword(plainData []byte, password str
return outBuf.String(), nil
}
//DecryptAttachmentWithPassword ...
//Decrypt attachment using password locked key.
func (pm *PmCrypto) DecryptAttachmentWithPassword(keyPacket []byte, dataPacket []byte, password string) ([]byte, error) {
encrypted := append(keyPacket, dataPacket...)

View file

@ -3,25 +3,266 @@ package crypto
import (
"bytes"
"crypto"
"encoding/base64"
"encoding/hex"
"errors"
"strings"
"time"
"fmt"
"github.com/ProtonMail/go-pm-crypto/armor"
"io"
"io/ioutil"
"math/big"
"time"
// "net/http"
// "net/url"
"strings"
//"github.com/ProtonMail/go-pm-crypto/armor"
"github.com/ProtonMail/go-pm-crypto/models"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/packet"
"math/big"
)
const (
ok = 0
notSigned = 1
noVerifier = 2
failed = 3
)
// A decrypted session key.
type SymmetricKey struct {
// The clear base64-encoded key.
//Key string
Key []byte
// The algorithm used by this key.
Algo string
}
//IsKeyExpiredBin ...
//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 cipher function corresponding to an algorithm used in this SymmetricKey
func (sk *SymmetricKey) GetCipherFunc() packet.CipherFunction {
cf, ok := symKeyAlgos[sk.Algo]
if ok {
return cf
}
panic("pmapi: unsupported cipher function: " + sk.Algo)
}
// Returns a key as base64 encoded string
func (sk *SymmetricKey) GetBase64Key() string {
return base64.StdEncoding.EncodeToString(sk.Key)
}
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: ek.Key, //base64.StdEncoding.EncodeToString(ek.Key),
Algo: algo,
}
}
// Decrypt and return a symmetric key
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
}
// Separate key and data packets in a pgp session
func SeparateKeyAndData(kr *KeyRing, r io.Reader) (outSplit *models.EncryptedSplit, err error) {
packets := packet.NewReader(r)
outSplit = &models.EncryptedSplit{}
// 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
if kr != nil {
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)
var symEncryptedData []byte
symEncryptedData = append(symEncryptedData, byte(210))
symEncryptedData = append(symEncryptedData, encodedLength...)
symEncryptedData = append(symEncryptedData, byte(1))
symEncryptedData = append(symEncryptedData, packetContents...)
outSplit.DataPacket = symEncryptedData
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
}*/
if kr == nil {
var buf bytes.Buffer
ek.Serialize(&buf)
outSplit.KeyPacket = buf.Bytes()
} else {
key := newSymmetricKey(ek)
outSplit.KeyPacket = key.Key
outSplit.Algo = key.Algo
}
return outSplit, nil
}
//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.GetCipherFunc()
//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, symKey.Key, 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
}
//Check if the given key is expired. Input in binary format
func (pm *PmCrypto) IsKeyExpiredBin(publicKey []byte) (bool, error) {
now := pm.getNow()
pubKeyReader := bytes.NewReader(publicKey)
@ -73,8 +314,14 @@ func (pm *PmCrypto) IsKeyExpiredBin(publicKey []byte) (bool, error) {
return true, errors.New("keys expired")
}
//IsKeyExpired ....
// will user the cached time to check
const (
ok = 0
notSigned = 1
noVerifier = 2
failed = 3
)
//Check if the given key is expired. Input in armored form
func (pm *PmCrypto) IsKeyExpired(publicKey string) (bool, error) {
rawPubKey, err := armor.Unarmor(publicKey)
if err != nil {
@ -170,7 +417,7 @@ func (pm *PmCrypto) GenerateKey(userName string, domain string, passphrase strin
return pm.generateKey(userName, domain, passphrase, keyType, bits, nil, nil, nil, nil)
}
// UpdatePrivateKeyPassphrase ...
// Decrypt given private key with oldPhrase and reencrypt with newPassphrase
func (pm *PmCrypto) UpdatePrivateKeyPassphrase(privateKey string, oldPassphrase string, newPassphrase string) (string, error) {
privKey := strings.NewReader(privateKey)

View file

@ -35,33 +35,8 @@ func (pm *PmCrypto) DecryptMessage(encryptedText string, privateKey string, pass
// privateKey : unarmored private use to decrypt message could be mutiple keys
// passphrase : match with private key to decrypt message
func (pm *PmCrypto) DecryptMessageBinKey(encryptedText string, privateKey []byte, passphrase string) (string, error) {
privKey := bytes.NewReader(privateKey)
privKeyEntries, err := openpgp.ReadKeyRing(privKey)
if err != nil {
return "", err
}
encryptedio, err := internal.Unarmor(encryptedText)
if err != nil {
return "", err
}
rawPwd := []byte(passphrase)
for _, e := range privKeyEntries {
if e.PrivateKey != nil && e.PrivateKey.Encrypted {
e.PrivateKey.Decrypt(rawPwd)
}
for _, sub := range e.Subkeys {
if sub.PrivateKey != nil && sub.PrivateKey.Encrypted {
sub.PrivateKey.Decrypt(rawPwd)
}
}
}
config := &packet.Config{Time: pm.getTimeGenerator()}
md, err := openpgp.ReadMessage(encryptedio.Body, privKeyEntries, nil, config)
md, err := pm.decryptCore(encryptedText, nil, privateKey, passphrase, pm.getTimeGenerator())
if err != nil {
return "", err
}
@ -123,10 +98,8 @@ func (pm *PmCrypto) DecryptMessageVerifyBinKey(encryptedText string, verifierKey
return pm.decryptMessageVerifyAllBin(encryptedText, verifierKey, privateKeyRaw, passphrase, verifyTime)
}
// decryptMessageVerifyAllBin
// decrypt_message_verify_single_key(private_key: string, passphras: string, encrypted : string, signature : string) : decrypt_sign_verify;
// decrypt_message_verify(passphras: string, encrypted : string, signature : string) : decrypt_sign_verify;
func (pm *PmCrypto) decryptMessageVerifyAllBin(encryptedText string, verifierKey []byte, privateKey []byte, passphrase string, verifyTime int64) (*models.DecryptSignedVerify, error) {
func (pm *PmCrypto) decryptCore(encryptedText string, additionalEntries openpgp.EntityList, privateKey []byte, passphrase string, timeFunc func() time.Time) (*openpgp.MessageDetails, error) {
privKey := bytes.NewReader(privateKey)
privKeyEntries, err := openpgp.ReadKeyRing(privKey)
if err != nil {
@ -147,22 +120,10 @@ func (pm *PmCrypto) decryptMessageVerifyAllBin(encryptedText string, verifierKey
}
}
out := &models.DecryptSignedVerify{}
out.Verify = failed
var verifierEntries openpgp.EntityList
if len(verifierKey) > 0 {
verifierReader := bytes.NewReader(verifierKey)
verifierEntries, err = openpgp.ReadKeyRing(verifierReader)
if err != nil {
return nil, err
}
for _, e := range verifierEntries {
if additionalEntries != nil {
for _, e := range additionalEntries {
privKeyEntries = append(privKeyEntries, e)
}
} else {
out.Verify = noVerifier
}
encryptedio, err := internal.Unarmor(encryptedText)
@ -170,16 +131,35 @@ func (pm *PmCrypto) decryptMessageVerifyAllBin(encryptedText string, verifierKey
return nil, err
}
config := &packet.Config{}
config.Time = func() time.Time {
return time.Unix(0, 0)
}
config := &packet.Config{Time: timeFunc}
md, err := openpgp.ReadMessage(encryptedio.Body, privKeyEntries, nil, config)
if err != nil {
return nil, err
return md, err
}
// decryptMessageVerifyAllBin
// decrypt_message_verify_single_key(private_key: string, passphras: string, encrypted : string, signature : string) : decrypt_sign_verify;
// decrypt_message_verify(passphras: string, encrypted : string, signature : string) : decrypt_sign_verify;
func (pm *PmCrypto) decryptMessageVerifyAllBin(encryptedText string, verifierKey []byte, privateKey []byte, passphrase string, verifyTime int64) (*models.DecryptSignedVerify, error) {
out := &models.DecryptSignedVerify{}
out.Verify = failed
var verifierEntries openpgp.EntityList
if len(verifierKey) > 0 {
verifierReader := bytes.NewReader(verifierKey)
var err error
verifierEntries, err = openpgp.ReadKeyRing(verifierReader)
if err != nil {
return nil, err
}
} else {
out.Verify = noVerifier
}
md, err := pm.decryptCore(encryptedText, verifierEntries, privateKey, passphrase, func() time.Time { return time.Unix(0, 0) }) // TODO: I doubt this time is correct
decrypted := md.UnverifiedBody
b, err := ioutil.ReadAll(decrypted)
if err != nil {
@ -256,7 +236,7 @@ func (pm *PmCrypto) EncryptMessageBinKey(plainText string, publicKey []byte, pri
plainText = internal.TrimNewlines(plainText)
}
var outBuf bytes.Buffer
w, err := armor.Encode(&outBuf, armorUtils.MESSAGE_HEADER, internal.ArmorHeaders)
w, err := armor.Encode(&outBuf, armorUtils.PGP_MESSAGE_HEADER, internal.ArmorHeaders)
if err != nil {
return "", err
}
@ -294,9 +274,7 @@ func (pm *PmCrypto) EncryptMessageBinKey(plainText string, publicKey []byte, pri
}
}
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: pm.getTimeGenerator()}
ew, err := openpgp.Encrypt(w, pubKeyEntries, signEntity, nil, config)
ew, err := EncryptCore(w, pubKeyEntries, signEntity, "", false, pm.getTimeGenerator())
_, _ = ew.Write([]byte(plainText))
ew.Close()
@ -310,7 +288,7 @@ func (pm *PmCrypto) EncryptMessageBinKey(plainText string, publicKey []byte, pri
func (pm *PmCrypto) EncryptMessageWithPassword(plainText string, password string) (string, error) {
var outBuf bytes.Buffer
w, err := armor.Encode(&outBuf, armorUtils.MESSAGE_HEADER, internal.ArmorHeaders)
w, err := armor.Encode(&outBuf, armorUtils.PGP_MESSAGE_HEADER, internal.ArmorHeaders)
if err != nil {
return "", err
}

View file

@ -12,10 +12,6 @@ import (
"strings"
)
// ======================== Attachments Collector ==============
// Collect contents of all attachment parts and return
// them as a string
func (pm PmCrypto) parseMIME(mimeBody string, verifierKey []byte) (*pmmime.BodyCollector, int, []string, []string, error) {
pubKey := bytes.NewReader(verifierKey)
pubKeyEntries, err := openpgp.ReadKeyRing(pubKey)

View file

@ -1,39 +1,15 @@
// Package crypto contains all methods and classes needed for manipulation
// with underlying cryptographic operations. It uses lowlevel openpgp functions
// and provides higher level views. It uses models of messages, attachments
// and other higher-level entities
package crypto
import "time"
// PmCrypto structure to manage multiple address keys and user keys
// Called PGP crypto because it cannot have the same name as the package by gomobile's ridiculous rules.
// PmCrypto structure is used to manage server time shift. It should be also used for any
// other specific general cryptographic entities.
type PmCrypto struct {
//latestServerTime unix time cache
latestServerTime int64
latestClientTime time.Time
}
// //AddAddress add a new address to key ring
// //add a new address into addresses list
// func (pgp *PmCrypto) AddAddress(address *Address) (bool, error) {
// return true, errors.New("this is not implemented yet, will add this later")
// }
// //RemoveAddress remove address from the keyring
// //
// //#remove a exsit address from the list based on address id
// func (pgp *PmCrypto) RemoveAddress(addressID string) (bool, error) {
// return true, errors.New("this is not implemented yet, will add this later")
// }
// //CleanAddresses clear all addresses in keyring
// func (pgp *PmCrypto) CleanAddresses() (bool, error) {
// return true, errors.New("this is not implemented yet, will add this later")
// }
// //EncryptMessage encrypt message use address id
// func (pgp *PmCrypto) EncryptMessage(addressID string, plainText string, passphrase string, trim bool) (string, error) {
// return "", errors.New("this is not implemented yet, will add this later")
// }
// //DecryptMessage decrypt message, this will lookup all keys
// func (pgp *PmCrypto) DecryptMessage(encryptText string, passphras string) (string, error) {
// return "", errors.New("this is not implemented yet, will add this later")
// }

View file

@ -8,7 +8,6 @@ import (
"strings"
"github.com/ProtonMail/go-pm-crypto/armor"
"github.com/ProtonMail/go-pm-crypto/models"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/packet"
)
@ -35,7 +34,7 @@ func (pm *PmCrypto) RandomTokenWith(size int) ([]byte, error) {
}
//GetSessionFromKeyPacketBinkeys get session key no encoding in and out
func (pm *PmCrypto) GetSessionFromKeyPacketBinkeys(keyPackage []byte, privateKey []byte, passphrase string) (*models.SessionSplit, error) {
func (pm *PmCrypto) GetSessionFromKeyPacketBinkeys(keyPackage []byte, privateKey []byte, passphrase string) (*SymmetricKey, error) {
keyReader := bytes.NewReader(keyPackage)
packets := packet.NewReader(keyReader)
@ -76,7 +75,7 @@ func (pm *PmCrypto) GetSessionFromKeyPacketBinkeys(keyPackage []byte, privateKey
}
//GetSessionFromKeyPacket get session key no encoding in and out
func (pm *PmCrypto) GetSessionFromKeyPacket(keyPackage []byte, privateKey string, passphrase string) (*models.SessionSplit, error) {
func (pm *PmCrypto) GetSessionFromKeyPacket(keyPackage []byte, privateKey string, passphrase string) (*SymmetricKey, error) {
keyReader := bytes.NewReader(keyPackage)
packets := packet.NewReader(keyReader)
@ -117,7 +116,7 @@ func (pm *PmCrypto) GetSessionFromKeyPacket(keyPackage []byte, privateKey string
}
//KeyPacketWithPublicKey ...
func (pm *PmCrypto) KeyPacketWithPublicKey(sessionSplit *models.SessionSplit, publicKey string) ([]byte, error) {
func (pm *PmCrypto) KeyPacketWithPublicKey(sessionSplit *SymmetricKey, publicKey string) ([]byte, error) {
pubkeyRaw, err := armor.Unarmor(publicKey)
if err != nil {
return nil, err
@ -126,13 +125,13 @@ func (pm *PmCrypto) KeyPacketWithPublicKey(sessionSplit *models.SessionSplit, pu
}
// KeyPacketWithPublicKeyBin ...
func (pm *PmCrypto) KeyPacketWithPublicKeyBin(sessionSplit *models.SessionSplit, publicKey []byte) ([]byte, error) {
func (pm *PmCrypto) KeyPacketWithPublicKeyBin(sessionSplit *SymmetricKey, publicKey []byte) ([]byte, error) {
publicKeyReader := bytes.NewReader(publicKey)
pubKeyEntries, err := openpgp.ReadKeyRing(publicKeyReader)
outbuf := &bytes.Buffer{}
cf := cipherFunc(sessionSplit.Algo)
cf := sessionSplit.GetCipherFunc()
if len(pubKeyEntries) == 0 {
return nil, errors.New("cannot set key: key ring is empty")
@ -163,7 +162,7 @@ func (pm *PmCrypto) KeyPacketWithPublicKeyBin(sessionSplit *models.SessionSplit,
return nil, errors.New("cannot set key: no public key available")
}
if err = packet.SerializeEncryptedKey(outbuf, pub, cf, sessionSplit.Session, nil); err != nil {
if err = packet.SerializeEncryptedKey(outbuf, pub, cf, sessionSplit.Key, nil); err != nil {
err = fmt.Errorf("pmapi: cannot set key: %v", err)
return nil, errors.New("cannot set key: key ring is empty")
}
@ -171,7 +170,7 @@ func (pm *PmCrypto) KeyPacketWithPublicKeyBin(sessionSplit *models.SessionSplit,
}
//GetSessionFromSymmetricPacket ...
func (pm *PmCrypto) GetSessionFromSymmetricPacket(keyPackage []byte, password string) (*models.SessionSplit, error) {
func (pm *PmCrypto) GetSessionFromSymmetricPacket(keyPackage []byte, password string) (*SymmetricKey, error) {
keyReader := bytes.NewReader(keyPackage)
packets := packet.NewReader(keyReader)
@ -197,9 +196,9 @@ func (pm *PmCrypto) GetSessionFromSymmetricPacket(keyPackage []byte, password st
for _, s := range symKeys {
key, cipherFunc, err := s.Decrypt(pwdRaw)
if err == nil {
return &models.SessionSplit{
Session: key,
Algo: getAlgo(cipherFunc),
return &SymmetricKey{
Key: key,
Algo: getAlgo(cipherFunc),
}, nil
}
@ -210,10 +209,10 @@ func (pm *PmCrypto) GetSessionFromSymmetricPacket(keyPackage []byte, password st
}
// SymmetricKeyPacketWithPassword ...
func (pm *PmCrypto) SymmetricKeyPacketWithPassword(sessionSplit *models.SessionSplit, password string) ([]byte, error) {
func (pm *PmCrypto) SymmetricKeyPacketWithPassword(sessionSplit *SymmetricKey, password string) ([]byte, error) {
outbuf := &bytes.Buffer{}
cf := cipherFunc(sessionSplit.Algo)
cf := sessionSplit.GetCipherFunc()
if len(password) <= 0 {
return nil, errors.New("password can't be empty")
@ -225,32 +224,14 @@ func (pm *PmCrypto) SymmetricKeyPacketWithPassword(sessionSplit *models.SessionS
DefaultCipher: cf,
}
err := packet.SerializeSymmetricKeyEncryptedReuseKey(outbuf, sessionSplit.Session, pwdRaw, config)
err := packet.SerializeSymmetricKeyEncryptedReuseKey(outbuf, sessionSplit.Key, pwdRaw, config)
if err != nil {
return nil, err
}
return outbuf.Bytes(), nil
}
//symKeyAlgos ...
var symKeyAlgos = map[string]packet.CipherFunction{
"3des": packet.Cipher3DES,
"cast5": packet.CipherCAST5,
"aes128": packet.CipherAES128,
"aes192": packet.CipherAES192,
"aes256": packet.CipherAES256,
}
// Get cipher function.
func cipherFunc(algo string) packet.CipherFunction {
cf, ok := symKeyAlgos[algo]
if ok {
return cf
}
return packet.CipherAES256
}
func getSessionSplit(ek *packet.EncryptedKey) (*models.SessionSplit, error) {
func getSessionSplit(ek *packet.EncryptedKey) (*SymmetricKey, error) {
if ek == nil {
return nil, errors.New("can't decrypt key packet")
}
@ -266,9 +247,9 @@ func getSessionSplit(ek *packet.EncryptedKey) (*models.SessionSplit, error) {
return nil, errors.New("can't decrypt key packet key is nil")
}
return &models.SessionSplit{
Session: ek.Key,
Algo: algo,
return &SymmetricKey{
Key: ek.Key,
Algo: algo,
}, nil
}

View file

@ -58,7 +58,7 @@ func (pm *PmCrypto) SignTextDetached(plainText string, privateKey string, passph
return outBuf.String(), nil
}
// SignTextDetachedBinKey ...
// Sign detached text using binary key data
func (pm *PmCrypto) SignTextDetachedBinKey(plainText string, privateKey []byte, passphrase string, trim bool) (string, error) {
//sign with 0x01
var signEntity *openpgp.Entity
@ -103,7 +103,7 @@ func (pm *PmCrypto) SignTextDetachedBinKey(plainText string, privateKey []byte,
return outBuf.String(), nil
}
// SignBinDetached sign bin data
// Sign detached bin data using string key
func (pm *PmCrypto) SignBinDetached(plainData []byte, privateKey string, passphrase string) (string, error) {
//sign with 0x00
var signEntity *openpgp.Entity
@ -144,7 +144,7 @@ func (pm *PmCrypto) SignBinDetached(plainData []byte, privateKey string, passphr
return outBuf.String(), nil
}
// SignBinDetachedBinKey ...
// Sign detached binary data using binary key format
func (pm *PmCrypto) SignBinDetachedBinKey(plainData []byte, privateKey []byte, passphrase string) (string, error) {
//sign with 0x00
var signEntity *openpgp.Entity
@ -185,7 +185,7 @@ func (pm *PmCrypto) SignBinDetachedBinKey(plainData []byte, privateKey []byte, p
return outBuf.String(), nil
}
// VerifyTextSignDetached ...
// Verify detached text - check if signature is valid using a given publicKey
func (pm *PmCrypto) VerifyTextSignDetached(signature string, plainText string, publicKey string, verifyTime int64) (bool, error) {
pubKeyReader := strings.NewReader(publicKey)
@ -202,7 +202,7 @@ func (pm *PmCrypto) VerifyTextSignDetached(signature string, plainText string, p
return verifySignature(pubKeyEntries, origText, signature, verifyTime)
}
// VerifyTextSignDetachedBinKey ...
// Verify detached text - check if signature is valid using a given publicKey in binary format
func (pm *PmCrypto) VerifyTextSignDetachedBinKey(signature string, plainText string, publicKey []byte, verifyTime int64) (bool, error) {
pubKeyReader := bytes.NewReader(publicKey)
@ -261,7 +261,7 @@ func verifySignature(pubKeyEntries openpgp.EntityList, origText *bytes.Reader, s
return true, nil
}
// VerifyBinSignDetached ...
// Verify detached text in binary format - check if signature is valid using a given publicKey in string format
func (pm *PmCrypto) VerifyBinSignDetached(signature string, plainData []byte, publicKey string, verifyTime int64) (bool, error) {
pubKeyReader := strings.NewReader(publicKey)
@ -275,7 +275,7 @@ func (pm *PmCrypto) VerifyBinSignDetached(signature string, plainData []byte, pu
return verifySignature(pubKeyEntries, origText, signature, verifyTime)
}
// VerifyBinSignDetachedBinKey ...
// Verify detached text in binary format - check if signature is valid using a given publicKey in binary format
func (pm *PmCrypto) VerifyBinSignDetachedBinKey(signature string, plainData []byte, publicKey []byte, verifyTime int64) (bool, error) {
pubKeyReader := bytes.NewReader(publicKey)

View file

@ -4,6 +4,12 @@ import (
"time"
)
var pmCrypto = PmCrypto{}
func GetPmCrypto() *PmCrypto {
return &pmCrypto
}
// UpdateTime update cached time
func (pm *PmCrypto) UpdateTime(newTime int64) {
pm.latestServerTime = newTime

View file

@ -1,3 +1,4 @@
// Provides key manipulation helper methods
package key
import (

View file

@ -1,3 +1,4 @@
// Provides high-level public data models used for communication mainly with mobile clients
package models
//EncryptedSplit when encrypt attachemt
@ -7,12 +8,6 @@ type EncryptedSplit struct {
Algo string
}
//SessionSplit split session
type SessionSplit struct {
Session []byte
Algo string
}
//EncryptedSigned encrypt_sign_package
type EncryptedSigned struct {
Encrypted string

View file

@ -1,235 +0,0 @@
package pmapi
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"io"
"io/ioutil"
// "net/http"
// "net/url"
"strings"
//"github.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
}

View file

@ -1,557 +0,0 @@
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"
armorUtils "github.com/ProtonMail/go-pm-crypto/armor"
"github.com/ProtonMail/go-pm-crypto/crypto"
)
// 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 := armorUtils.ArmorWithTypeBuffered(w, armorUtils.MESSAGE_HEADER)
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 != armorUtils.MESSAGE_HEADER {
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
}

View file

@ -1,283 +0,0 @@
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
xcMGBFRJbc0BCAC0mMLZPDBbtSCWvxwmOfXfJkE2+ssM3ux21LhD/bPiWefE
WSHlCjJ8PqPHy7snSiUuxuj3f9AvXPvg+mjGLBwu1/QsnSP24sl3qD2onl39
vPiLJXUqZs20ZRgnvX70gjkgEzMFBxINiy2MTIG+4RU8QA7y8KzWev0btqKi
MeVa+GLEHhgZ2KPOn4Jv1q4bI9hV0C9NUe2tTXS6/Vv3vbCY7lRR0kbJ65T5
c8CmpqJuASIJNrSXM/Q3NnnsY4kBYH0s5d2FgbASQvzrjuC2rngUg0EoPsrb
DEVRA2/BCJonw7aASiNCrSP92lkZdtYlax/pcoE/mQ4WSwySFmcFT7yFABEB
AAH+CQMIvzcDReuJkc9gnxAkfgmnkBFwRQrqT/4UAPOF8WGVo0uNvDo7Snlk
qWsJS+54+/Xx6Jur/PdBWeEu+6+6GnppYuvsaT0D0nFdFhF6pjng+02IOxfG
qlYXYcW4hRru3BfvJlSvU2LL/Z/ooBnw3T5vqd0eFHKrvabUuwf0x3+K/sru
Fp24rl2PU+bzQlUgKpWzKDmO+0RdKQ6KVCyCDMIXaAkALwNffAvYxI0wnb2y
WAV/bGn1ODnszOYPk3pEMR6kKSxLLaO69kYx4eTERFyJ+1puAxEPCk3Cfeif
yDWi4rU03YB16XH7hQLSFl61SKeIYlkKmkO5Hk1ybi/BhvOGBPVeGGbxWnwI
46G8DfBHW0+uvD5cAQtk2d/q3Ge1I+DIyvuRCcSu0XSBNv/Bkpp4IbAUPBaW
TIvf5p9oxw+AjrMtTtcdSiee1S6CvMMaHhVD7SI6qGA8GqwaXueeLuEXa0Ok
BWlehx8wibMi4a9fLcQZtzJkmGhR1WzXcJfiEg32srILwIzPQYxuFdZZ2elb
gYp/bMEIp4LKhi43IyM6peCDHDzEba8NuOSd0heEqFIm0vlXujMhkyMUvDBv
H0V5On4aMuw/aSEKcAdbazppOru/W1ndyFa5ZHQIC19g72ZaDVyYjPyvNgOV
AFqO4o3IbC5z31zMlTtMbAq2RG9svwUVejn0tmF6UPluTe0U1NuXFpLK6TCH
wqocLz4ecptfJQulpYjClVLgzaYGDuKwQpIwPWg5G/DtKSCGNtEkfqB3aemH
V5xmoYm1v5CQZAEvvsrLA6jxCk9lzqYV8QMivWNXUG+mneIEM35G0HOPzXca
LLyB+N8Zxioc9DPGfdbcxXuVgOKRepbkq4xv1pUpMQ4BUmlkejDRSP+5SIR3
iEthg+FU6GRSQbORE6nhrKjGBk8fpNpozQZVc2VySUTCwHIEEAEIACYFAlRJ
bc8GCwkIBwMCCRA+tiWe3yHfJAQVCAIKAxYCAQIbAwIeAQAA9J0H/RLR/Uwt
CakrPKtfeGaNuOI45SRTNxM8TklC6tM28sJSzkX8qKPzvI1PxyLhs/i0/fCQ
7Z5bU6n41oLuqUt2S9vy+ABlChKAeziOqCHUcMzHOtbKiPkKW88aO687nx+A
ol2XOnMTkVIC+edMUgnKp6tKtZnbO4ea6Cg88TFuli4hLHNXTfCECswuxHOc
AO1OKDRrCd08iPI5CLNCIV60QnduitE1vF6ehgrH25Vl6LEdd8vPVlTYAvsa
6ySk2RIrHNLUZZ3iII3MBFL8HyINp/XA1BQP+QbH801uSLq8agxM4iFT9C+O
D147SawUGhjD5RG7T+YtqItzgA1V9l277EXHwwYEVEltzwEIAJD57uX6bOc4
Tgf3utfL/4hdyoqIMVHkYQOvE27wPsZxX08QsdlaNeGji9Ap2ifIDuckUqn6
Ji9jtZDKtOzdTBm6rnG5nPmkn6BJXPhnecQRP8N0XBISnAGmE4t+bxtts5Wb
qeMdxJYqMiGqzrLBRJEIDTcg3+QF2Y3RywOqlcXqgG/xX++PsvR1Jiz0rEVP
TcBc7ytyb/Av7mx1S802HRYGJHOFtVLoPTrtPCvv+DRDK8JzxQW2XSQLlI0M
9s1tmYhCogYIIqKx9qOTd5mFJ1hJlL6i9xDkvE21qPFASFtww5tiYmUfFaxI
LwbXPZlQ1I/8fuaUdOxctQ+g40ZgHPcAEQEAAf4JAwgdUg8ubE2BT2DITBD+
XFgjrnUlQBilbN8/do/36KHuImSPO/GGLzKh4+oXxrvLc5fQLjeO+bzeen4u
COCBRO0hG7KpJPhQ6+T02uEF6LegE1sEz5hp6BpKUdPZ1+8799Rylb5kubC5
IKnLqqpGDbH3hIsmSV3CG/ESkaGMLc/K0ZPt1JRWtUQ9GesXT0v6fdM5GB/L
cZWFdDoYgZAw5BtymE44knIodfDAYJ4DHnPCh/oilWe1qVTQcNMdtkpBgkuo
THecqEmiODQz5EX8pVmS596XsnPO299Lo3TbaHUQo7EC6Au1Au9+b5hC1pDa
FVCLcproi/Cgch0B/NOCFkVLYmp6BEljRj2dSZRWbO0vgl9kFmJEeiiH41+k
EAI6PASSKZs3BYLFc2I8mBkcvt90kg4MTBjreuk0uWf1hdH2Rv8zprH4h5Uh
gjx5nUDX8WXyeLxTU5EBKry+A2DIe0Gm0/waxp6lBlUl+7ra28KYEoHm8Nq/
N9FCuEhFkFgw6EwUp7jsrFcqBKvmni6jyplm+mJXi3CK+IiNcqub4XPnBI97
lR19fupB/Y6M7yEaxIM8fTQXmP+x/fe8zRphdo+7o+pJQ3hk5LrrNPK8GEZ6
DLDOHjZzROhOgBvWtbxRktHk+f5YpuQL+xWd33IV1xYSSHuoAm0Zwt0QJxBs
oFBwJEq1NWM4FxXJBogvzV7KFhl/hXgtvx+GaMv3y8gucj+gE89xVv0XBXjl
5dy5/PgCI0Id+KAFHyKpJA0N0h8O4xdJoNyIBAwDZ8LHt0vlnLGwcJFR9X7/
PfWe0PFtC3d7cYY3RopDhnRP7MZs1Wo9nZ4IvlXoEsE2nPkWcns+Wv5Yaewr
s2ra9ZIK7IIJhqKKgmQtCeiXyFwTq+kfunDnxeCavuWL3HuLKIOZf7P9vXXt
XgEir9rCwF8EGAEIABMFAlRJbdIJED62JZ7fId8kAhsMAAD+LAf+KT1EpkwH
0ivTHmYako+6qG6DCtzd3TibWw51cmbY20Ph13NIS/MfBo828S9SXm/sVUzN
/r7qZgZYfI0/j57tG3BguVGm53qya4bINKyi1RjK6aKo/rrzRkh5ZVD5rVNO
E2zzvyYAnLUWG9AV1OYDxcgLrXqEMWlqZAo+Wmg7VrTBmdCGs/BPvscNgQRr
6Gpjgmv9ru6LjRL7vFhEcov/tkBLj+CtaWWFTd1s2vBLOs4rCsD9TT/23vfw
CnokvvVjKYN5oviy61yhpqF1rWlOsxZ4+2sKW3Pq7JLBtmzsZegTONfcQAf7
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)
}
}