diff --git a/armor/armor.go b/armor/armor.go index 50df2b7..f4fab46 100644 --- a/armor/armor.go +++ b/armor/armor.go @@ -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 -} diff --git a/armor/constants.go b/armor/constants.go index 483fe9f..ae342fd 100644 --- a/armor/constants.go +++ b/armor/constants.go @@ -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" ) diff --git a/crypto/attachment.go b/crypto/attachment.go index 8fe23d6..2a81114 100644 --- a/crypto/attachment.go +++ b/crypto/attachment.go @@ -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...) diff --git a/crypto/key.go b/crypto/key.go index 9a93524..de17d35 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -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) diff --git a/crypto/message.go b/crypto/message.go index 304033d..5b7c5a1 100644 --- a/crypto/message.go +++ b/crypto/message.go @@ -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 } diff --git a/crypto/mime.go b/crypto/mime.go index a22b88e..ccc4ebc 100644 --- a/crypto/mime.go +++ b/crypto/mime.go @@ -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) diff --git a/crypto/pmcrypto.go b/crypto/pmcrypto.go index 504b309..239b253 100644 --- a/crypto/pmcrypto.go +++ b/crypto/pmcrypto.go @@ -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") -// } diff --git a/crypto/session.go b/crypto/session.go index 7b0aa04..bc835ec 100644 --- a/crypto/session.go +++ b/crypto/session.go @@ -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 } diff --git a/crypto/sign_detached.go b/crypto/sign_detached.go index b940e3d..6276003 100644 --- a/crypto/sign_detached.go +++ b/crypto/sign_detached.go @@ -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) diff --git a/crypto/time.go b/crypto/time.go index 57af22b..366d334 100644 --- a/crypto/time.go +++ b/crypto/time.go @@ -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 diff --git a/key/key.go b/key/key.go index e805be6..27f0369 100644 --- a/key/key.go +++ b/key/key.go @@ -1,3 +1,4 @@ +// Provides key manipulation helper methods package key import ( diff --git a/models/models.go b/models/models.go index 9f6ea50..2fa65b9 100644 --- a/models/models.go +++ b/models/models.go @@ -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 diff --git a/pmapi/key.go b/pmapi/key.go deleted file mode 100644 index 64ced80..0000000 --- a/pmapi/key.go +++ /dev/null @@ -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 -} diff --git a/pmapi/keyring.go b/pmapi/keyring.go deleted file mode 100644 index 2fb7607..0000000 --- a/pmapi/keyring.go +++ /dev/null @@ -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 -} diff --git a/pmapi/keyring_test.go b/pmapi/keyring_test.go deleted file mode 100644 index 870489a..0000000 --- a/pmapi/keyring_test.go +++ /dev/null @@ -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 - -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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) - } -}