commit 8d300e4782cd41a97e7dd6f55719466247f7a710 Author: zhj4478 Date: Mon Jun 4 16:05:14 2018 -0700 wrapper for mobile diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..99b3bbb --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +bin +vendor +.vscode \ No newline at end of file diff --git a/armor.go b/armor.go new file mode 100644 index 0000000..4c993ed --- /dev/null +++ b/armor.go @@ -0,0 +1,61 @@ +package pm + +import ( + "bytes" + "io/ioutil" + "strings" + + "golang.org/x/crypto/openpgp/armor" +) + +// AromrType ... +type AromrType string + +func (at AromrType) string() string { + return string(at) +} + +const ( + pgpMessageType AromrType = "PGP MESSAGE" + pgpPublicBlockType AromrType = "PGP PUBLIC KEY BLOCK" + pgpPrivateBlockType AromrType = "PGP PRIVATE KEY BLOCK" +) + +// ArmorKey make bytes input key to armor format +func ArmorKey(input []byte) (string, error) { + return ArmorWithType(input, pgpPublicBlockType.string()) +} + +// 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, nil) + if err != nil { + return "", err + } + _, err = w.Write(input) + if err != nil { + return "", err + } + w.Close() + return b.String(), nil +} + +// UnArmor an armored key to bytes key +func UnArmor(input string) ([]byte, error) { + b, err := unArmor(input) + if err != nil { + return nil, err + } + return ioutil.ReadAll(b.Body) +} + +func unArmor(input string) (*armor.Block, error) { + io := strings.NewReader(input) + b, err := armor.Decode(io) + if err != nil { + return nil, err + } + return b, nil + +} diff --git a/attachment.go b/attachment.go new file mode 100644 index 0000000..2d426c8 --- /dev/null +++ b/attachment.go @@ -0,0 +1,158 @@ +package pm + +import ( + "bytes" + "io" + "io/ioutil" + + "golang.org/x/crypto/openpgp" + "golang.org/x/crypto/openpgp/armor" + "golang.org/x/crypto/openpgp/packet" +) + +//EncryptAttachmentBinKey ... +func (o *OpenPGP) EncryptAttachmentBinKey(plainData []byte, fileName string, publicKey []byte) (*EncryptedSplit, error) { + + var outBuf bytes.Buffer + w, err := armor.Encode(&outBuf, pgpMessageType.string(), armorHeader) + if err != nil { + return nil, err + } + + pubKeyReader := bytes.NewReader(publicKey) + pubKeyEntries, err := openpgp.ReadKeyRing(pubKeyReader) + if err != nil { + return nil, err + } + hints := &openpgp.FileHints{ + FileName: fileName, + } + config := &packet.Config{DefaultCipher: packet.CipherAES256} + + ew, err := openpgp.Encrypt(w, pubKeyEntries, nil, hints, config) + + _, _ = ew.Write(plainData) + ew.Close() + w.Close() + + splited, err := SeparateKeyAndData(outBuf.String()) + if err != nil { + return nil, err + } + splited.Algo = "aes256" + return splited, nil +} + +//EncryptAttachment ... +func (o *OpenPGP) EncryptAttachment(plainData []byte, fileName string, publicKey string) (*EncryptedSplit, error) { + rawPubKey, err := UnArmor(publicKey) + if err != nil { + return nil, err + } + return o.EncryptAttachmentBinKey(plainData, fileName, rawPubKey) +} + +//DecryptAttachmentBinKey ... +//keyPacket +//dataPacket +//privateKeys could be mutiple private keys +func (o *OpenPGP) DecryptAttachmentBinKey(keyPacket []byte, dataPacket []byte, privateKeys []byte, passphrase string) ([]byte, error) { + privKeyRaw := bytes.NewReader(privateKeys) + privKeyEntries, err := openpgp.ReadKeyRing(privKeyRaw) + if err != nil { + return nil, 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) + } + } + } + + keyReader := bytes.NewReader(keyPacket) + dataReader := bytes.NewReader(dataPacket) + + encryptedReader := io.MultiReader(keyReader, dataReader) + + md, err := openpgp.ReadMessage(encryptedReader, privKeyEntries, nil, nil) + if err != nil { + return nil, err + } + + decrypted := md.UnverifiedBody + b, err := ioutil.ReadAll(decrypted) + if err != nil { + return nil, err + } + + return b, nil +} + +//DecryptAttachment ... +func (o *OpenPGP) DecryptAttachment(keyPacket []byte, dataPacket []byte, privateKey string, passphrase string) ([]byte, error) { + rawPrivKey, err := UnArmor(privateKey) + if err != nil { + return nil, err + } + return o.DecryptAttachmentBinKey(keyPacket, dataPacket, rawPrivKey, passphrase) +} + +//EncryptAttachmentWithPassword ... +func (o *OpenPGP) EncryptAttachmentWithPassword(plainData []byte, password string) (string, error) { + + var outBuf bytes.Buffer + w, err := armor.Encode(&outBuf, pgpMessageType.string(), armorHeader) + if err != nil { + return "", err + } + + plaintext, err := openpgp.SymmetricallyEncrypt(w, []byte(password), nil, nil) + if err != nil { + return "", err + } + + _, err = plaintext.Write(plainData) + if err != nil { + return "", err + } + err = plaintext.Close() + if err != nil { + return "", err + } + w.Close() + + return outBuf.String(), nil +} + +//DecryptAttachmentWithPassword ... +func (o *OpenPGP) DecryptAttachmentWithPassword(keyPacket []byte, dataPacket []byte, password string) ([]byte, error) { + + encrypted := append(keyPacket, dataPacket...) + + encryptedReader := bytes.NewReader(encrypted) + + var prompt = func(keys []openpgp.Key, symmetric bool) ([]byte, error) { + return []byte(password), nil + } + + md, err := openpgp.ReadMessage(encryptedReader, nil, prompt, nil) + if err != nil { + return nil, err + } + + messageBuf := bytes.NewBuffer(nil) + _, err = io.Copy(messageBuf, md.UnverifiedBody) + if err != nil { + return nil, err + } + + return messageBuf.Bytes(), nil +} diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..dff964b --- /dev/null +++ b/build.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +SCRIPT_LOCATION=$(cd $(dirname $0);echo $PWD) + +OUTPUT_PATH="bin" +ANDROID_OUT=${OUTPUT_PATH}/"Android" +IOS_OUT=${OUTPUT_PATH}/"iOS" + +# CHECK="${1-0}" +# if [ ${CHECK} -eq "1" ]; then +printf "\e[0;32mStart Building iOS framework .. Location: ${IOS_OUT} \033[0m\n\n" + +gomobile bind -target ios -o ${IOS_OUT}/PM.framework + + +printf "\e[0;32mStart Building Android lib .. Location: ${ANDROID_OUT} \033[0m\n\n" + +gomobile bind -target android -o ${ANDROID_OUT}/PM.aar + + +printf "\e[0;32mInstalling frameworks. \033[0m\n\n" + +cp -rf ${IOS_OUT}/PM.framework /Users/Yanfeng/Documents/ProtonMailGit/protonmail_ios/ProtonMail/ + +printf "\e[0;32mAll Done. \033[0m\n\n" + + diff --git a/common.go b/common.go new file mode 100644 index 0000000..25758f3 --- /dev/null +++ b/common.go @@ -0,0 +1,23 @@ +package pm + +var armorHeader = map[string]string{ + "Version": "OpenPGP Mobile 0.0.1 (" + Version() + ")", + "Comment": "https://protonmail.com", +} + +// Key ... add later +// protonmail key object +type Key struct { + KeyID string + PublicKey string + PrivateKey string + FingerPrint string +} + +//Address ... add later protonmail address object +type Address struct { + // address_id : string; + // #optional + // address_name : string; + keys []Key +} diff --git a/fingerprint.go b/fingerprint.go new file mode 100644 index 0000000..b89ddf7 --- /dev/null +++ b/fingerprint.go @@ -0,0 +1,32 @@ +package pm + +import ( + "bytes" + "encoding/hex" + "errors" + + "golang.org/x/crypto/openpgp" +) + +// GetFingerprint get a armored public key fingerprint +func GetFingerprint(publicKey string) (string, error) { + rawPubKey, err := UnArmor(publicKey) + if err != nil { + return "", err + } + return GetFingerprintBinKey(rawPubKey) +} + +// GetFingerprintBinKey get a unarmored public key fingerprint +func GetFingerprintBinKey(publicKey []byte) (string, error) { + pubKeyReader := bytes.NewReader(publicKey) + pubKeyEntries, err := openpgp.ReadKeyRing(pubKeyReader) + if err != nil { + return "", err + } + for _, e := range pubKeyEntries { + fp := e.PrimaryKey.Fingerprint + return hex.EncodeToString(fp[:]), nil + } + return "", errors.New("Can't find public key") +} diff --git a/glide.lock b/glide.lock new file mode 100644 index 0000000..d7c48a9 --- /dev/null +++ b/glide.lock @@ -0,0 +1,26 @@ +hash: 217e5bc8c4d3160eeddd18dda7f8f8785f1b2b7f2ca58779b94ed8fd91ab226d +updated: 2018-06-04T16:04:51.812734-07:00 +imports: +- name: golang.org/x/crypto + version: d36f3ee340e0aa6549738d42e0cb9139c902d7fd + repo: https://github.com/ProtonMail/crypto.git + subpackages: + - bitcurves + - brainpool + - cast5 + - curve25519 + - ed25519 + - ed25519/internal/edwards25519 + - openpgp + - openpgp/aes/keywrap + - openpgp/armor + - openpgp/clearsign + - openpgp/ecdh + - openpgp/elgamal + - openpgp/errors + - openpgp/internal/algorithm + - openpgp/internal/ecc + - openpgp/internal/encoding + - openpgp/packet + - openpgp/s2k +testImports: [] diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 0000000..fdc8b93 --- /dev/null +++ b/glide.yaml @@ -0,0 +1,5 @@ +package: pm +import: +- package: golang.org/x/crypto + version: master + repo: https://github.com/ProtonMail/crypto.git diff --git a/key.go b/key.go new file mode 100644 index 0000000..e860d20 --- /dev/null +++ b/key.go @@ -0,0 +1,298 @@ +package pm + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" + "strings" + "time" + + "golang.org/x/crypto/openpgp" + "golang.org/x/crypto/openpgp/packet" +) + +//EncryptedSplit when encrypt attachemt +type EncryptedSplit struct { + DataPacket []byte + KeyPacket []byte + Algo string +} + +//SessionSplit splited session +type SessionSplit struct { + Session []byte + Algo string +} + +//EncryptedSigned encrypt_sign_package +type EncryptedSigned struct { + Encrypted string + Signature string +} + +const ( + ok = 0 + notSigned = 1 + noVerifier = 2 + failed = 3 +) + +//DecryptSignedVerify decrypt_sign_verify +type DecryptSignedVerify struct { + //clear text + Plaintext string + //bitmask verify status : 0 + Verify int + //error message if verify failed + Message string +} + +//CheckPassphrase check is private key passphrase ok +func CheckPassphrase(privateKey string, passphrase string) bool { + privKeyReader := strings.NewReader(privateKey) + entries, err := openpgp.ReadArmoredKeyRing(privKeyReader) + if err != nil { + fmt.Println(err) + return false + } + + var keys []*packet.PrivateKey + + for _, e := range entries { + keys = append(keys, e.PrivateKey) + } + var decryptError error + var n int + for _, key := range keys { + if !key.Encrypted { + continue // Key already decrypted + } + if decryptError = key.Decrypt([]byte(passphrase)); decryptError == nil { + n++ + } + } + if n == 0 { + return false + } + return true +} + +//IsKeyExpiredBin ... +func (o *OpenPGP) IsKeyExpiredBin(publicKey []byte) (bool, error) { + now := o.getNow() + pubKeyReader := bytes.NewReader(publicKey) + pubKeyEntries, err := openpgp.ReadKeyRing(pubKeyReader) + if err != nil { + return true, err + } + candidateSubkey := -1 + for _, e := range pubKeyEntries { + var maxTime time.Time + for i, subkey := range e.Subkeys { + if subkey.Sig.FlagsValid && + subkey.Sig.FlagEncryptCommunications && + subkey.PublicKey.PubKeyAlgo.CanEncrypt() && + !subkey.Sig.KeyExpired(now) && + (maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) { + candidateSubkey = i + maxTime = subkey.Sig.CreationTime + } + } + + if candidateSubkey != -1 { + return false, nil + } + + // If we don't have any candidate subkeys for encryption and + // the primary key doesn't have any usage metadata then we + // assume that the primary key is ok. Or, if the primary key is + // marked as ok to encrypt to, then we can obviously use it. + var firstIdentity *openpgp.Identity + for _, ident := range e.Identities { + if firstIdentity == nil { + firstIdentity = ident + } + if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { + firstIdentity = ident + break + } + } + if firstIdentity != nil { + i := firstIdentity + if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && + e.PrimaryKey.PubKeyAlgo.CanEncrypt() && + !i.SelfSignature.KeyExpired(now) { + return false, nil + } + } + } + return true, errors.New("keys expired") +} + +//IsKeyExpired .... +// will user the cached time to check +func (o *OpenPGP) IsKeyExpired(publicKey string) (bool, error) { + rawPubKey, err := UnArmor(publicKey) + if err != nil { + return false, err + } + return o.IsKeyExpiredBin(rawPubKey) +} + +// GenerateKey ... +// disabled now, will enable later +// #generat new key with email address. Fix the UserID issue in protonmail system. on Feb 28, 17 +// #static generate_key_with_email(email : string, passphrase : string, bits : i32) : open_pgp_key; +// # generate new key +// #static generate_new_key(user_id : string, email : string, passphrase : string, bits : i32) : open_pgp_key; +func (o *OpenPGP) GenerateKey(userName string, domain string, passphrase string, keyType string, bits int) (string, error) { + + if len(userName) <= 0 { + return "", errors.New("Invalid user name format") + } + if len(domain) <= 0 { + return "", errors.New("Invalid domain") + } + email := userName + "@" + domain + comments := "" + timeNow := func() time.Time { + return o.getNow() + } + + cfg := &packet.Config{RSABits: bits, Time: timeNow} + newEntity, err := openpgp.NewEntity(email, comments, email, cfg) + if err != nil { + return "", err + } + + if err := newEntity.SelfSign(nil); err != nil { + return "", err + } + + rawPwd := []byte(passphrase) + if newEntity.PrivateKey != nil && !newEntity.PrivateKey.Encrypted { + if err := newEntity.PrivateKey.Encrypt(rawPwd); err != nil { + return "", err + } + } + + for _, sub := range newEntity.Subkeys { + if sub.PrivateKey != nil && !sub.PrivateKey.Encrypted { + if err := sub.PrivateKey.Encrypt(rawPwd); err != nil { + return "", err + } + } + } + + w := bytes.NewBuffer(nil) + if err := newEntity.SerializePrivateNoSign(w, nil); err != nil { + return "", err + } + serialized := w.Bytes() + return ArmorWithType(serialized, pgpPrivateBlockType.string()) +} + +// UpdatePrivateKeyPassphrase ... +func (o *OpenPGP) UpdatePrivateKeyPassphrase(privateKey string, oldPassphrase string, newPassphrase string) (string, error) { + + privKey := strings.NewReader(privateKey) + privKeyEntries, err := openpgp.ReadArmoredKeyRing(privKey) + if err != nil { + return "", err + } + + oldrawPwd := []byte(oldPassphrase) + newRawPwd := []byte(newPassphrase) + w := bytes.NewBuffer(nil) + for _, e := range privKeyEntries { + if e.PrivateKey != nil && e.PrivateKey.Encrypted { + if err := e.PrivateKey.Decrypt(oldrawPwd); err != nil { + return "", err + } + } + if e.PrivateKey != nil && !e.PrivateKey.Encrypted { + if err := e.PrivateKey.Encrypt(newRawPwd); err != nil { + return "", err + } + } + + for _, sub := range e.Subkeys { + if sub.PrivateKey != nil && sub.PrivateKey.Encrypted { + if err := sub.PrivateKey.Decrypt(oldrawPwd); err != nil { + return "", err + } + } + if sub.PrivateKey != nil && !sub.PrivateKey.Encrypted { + if err := sub.PrivateKey.Encrypt(newRawPwd); err != nil { + return "", err + } + } + } + if err := e.SerializePrivateNoSign(w, nil); err != nil { + return "", err + } + } + + serialized := w.Bytes() + return ArmorWithType(serialized, pgpPrivateBlockType.string()) +} + +// PublicKey get a public key from a private key +func PublicKey(privateKey string) (string, error) { + privKeyReader := strings.NewReader(privateKey) + entries, err := openpgp.ReadArmoredKeyRing(privKeyReader) + if err != nil { + return "", err + } + + var outBuf bytes.Buffer + for _, e := range entries { + e.Serialize(&outBuf) + } + + outString, err := ArmorKey(outBuf.Bytes()) + if err != nil { + return "", nil + } + + return outString, nil +} + +// PublicKeyBinOut get a public key from a private key +func PublicKeyBinOut(privateKey string) ([]byte, error) { + privKeyReader := strings.NewReader(privateKey) + entries, err := openpgp.ReadArmoredKeyRing(privKeyReader) + if err != nil { + return nil, err + } + + var outBuf bytes.Buffer + for _, e := range entries { + e.Serialize(&outBuf) + } + + return outBuf.Bytes(), nil +} + +// CheckKey print out the key and subkey fingerprint +func CheckKey(pubKey string) (string, error) { + pubKeyReader := strings.NewReader(pubKey) + entries, err := openpgp.ReadArmoredKeyRing(pubKeyReader) + if err != nil { + return "", err + } + + for _, e := range entries { + for _, subKey := range e.Subkeys { + if !subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications { + + println("SubKey:" + hex.EncodeToString(subKey.PublicKey.Fingerprint[:])) + + } + } + println("PrimaryKey:" + hex.EncodeToString(e.PrimaryKey.Fingerprint[:])) + + } + return "", nil +} diff --git a/message.go b/message.go new file mode 100644 index 0000000..fcadb15 --- /dev/null +++ b/message.go @@ -0,0 +1,322 @@ +package pm + +import ( + "bytes" + "errors" + "io" + "io/ioutil" + "strings" + "time" + + "golang.org/x/crypto/openpgp" + "golang.org/x/crypto/openpgp/armor" + "golang.org/x/crypto/openpgp/packet" +) + +// DecryptMessage decrypt encrypted message use private key (string ) +// encryptedText : string armored encrypted +// privateKey : armored private use to decrypt message +// passphrase : match with private key to decrypt message +func (o *OpenPGP) DecryptMessage(encryptedText string, privateKey string, passphrase string) (string, error) { + privKeyRaw, err := UnArmor(privateKey) + if err != nil { + return "", err + } + return o.DecryptMessageBinKey(encryptedText, privKeyRaw, passphrase) +} + +// DecryptMessageBinKey decrypt encrypted message use private key (bytes ) +// encryptedText : string armored encrypted +// privateKey : unarmored private use to decrypt message +// passphrase : match with private key to decrypt message +func (o *OpenPGP) 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 := 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) + } + } + } + + md, err := openpgp.ReadMessage(encryptedio.Body, privKeyEntries, nil, nil) + if err != nil { + return "", err + } + + decrypted := md.UnverifiedBody + b, err := ioutil.ReadAll(decrypted) + if err != nil { + return "", err + } + + println(4) + return string(b), nil +} + +// encryptedText string, privateKey string, passphrase string) (string, error) +// 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 (o *OpenPGP) DecryptMessageVerifyPrivbinkeys(encryptedText string, veriferKey string, privateKeys []byte, passphrase string, verifyTime int64) (*DecryptSignedVerify, error) { + + if len(veriferKey) > 0 { + verifierRaw, err := UnArmor(veriferKey) + if err != nil { + return nil, err + } + return o.decryptMessageVerifyAllBin(encryptedText, verifierRaw, privateKeys, passphrase, verifyTime) + } + return o.decryptMessageVerifyAllBin(encryptedText, nil, privateKeys, passphrase, verifyTime) +} + +// encryptedText string, privateKey string, passphrase string) (string, error) +// 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 (o *OpenPGP) DecryptMessageVerifyBinKeyPrivbinkeys(encryptedText string, veriferKey []byte, privateKeys []byte, passphrase string, verifyTime int64) (*DecryptSignedVerify, error) { + return o.decryptMessageVerifyAllBin(encryptedText, veriferKey, privateKeys, passphrase, verifyTime) +} + +// encryptedText string, privateKey string, passphrase string) (string, error) +// 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 (o *OpenPGP) DecryptMessageVerify(encryptedText string, veriferKey string, privateKey string, passphrase string, verifyTime int64) (*DecryptSignedVerify, error) { + if len(veriferKey) > 0 { + verifierRaw, err := UnArmor(veriferKey) + if err != nil { + return nil, err + } + return o.DecryptMessageVerifyBinKey(encryptedText, verifierRaw, privateKey, passphrase, verifyTime) + } + return o.DecryptMessageVerifyBinKey(encryptedText, nil, privateKey, passphrase, verifyTime) +} + +// encryptedText string, privateKey string, passphrase string) (string, error) +// 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 (o *OpenPGP) DecryptMessageVerifyBinKey(encryptedText string, veriferKey []byte, privateKey string, passphrase string, verifyTime int64) (*DecryptSignedVerify, error) { + privateKeyRaw, err := UnArmor(privateKey) + if err != nil { + return nil, err + } + return o.decryptMessageVerifyAllBin(encryptedText, veriferKey, privateKeyRaw, passphrase, verifyTime) +} + +// encryptedText string, privateKey string, passphrase string) (string, error) +// 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 (o *OpenPGP) decryptMessageVerifyAllBin(encryptedText string, veriferKey []byte, privateKey []byte, passphrase string, verifyTime int64) (*DecryptSignedVerify, error) { + privKey := bytes.NewReader(privateKey) + privKeyEntries, err := openpgp.ReadKeyRing(privKey) + if err != nil { + return nil, 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) + } + } + } + + out := &DecryptSignedVerify{} + out.Verify = failed + + if len(veriferKey) > 0 { + verifierReader := bytes.NewReader(veriferKey) + verifierEnties, err := openpgp.ReadKeyRing(verifierReader) + if err != nil { + return nil, err + } + + for _, e := range verifierEnties { + privKeyEntries = append(privKeyEntries, e) + } + } else { + out.Verify = noVerifier + } + + encryptedio, err := unArmor(encryptedText) + if err != nil { + return nil, err + } + + config := &packet.Config{} + if verifyTime > 0 { + tm := time.Unix(verifyTime, 0) + config.Time = func() time.Time { + return tm + } + } + + md, err := openpgp.ReadMessage(encryptedio.Body, privKeyEntries, nil, config) + if err != nil { + return nil, err + } + + decrypted := md.UnverifiedBody + b, err := ioutil.ReadAll(decrypted) + if err != nil { + return nil, err + } + + out.Plaintext = string(b) + if md.IsSigned { + if md.SignedBy != nil { + if md.SignatureError == nil { + out.Verify = ok + } else { + out.Message = md.SignatureError.Error() + out.Verify = failed + } + } else { + out.Verify = noVerifier + } + } else { + out.Verify = notSigned + } + return out, nil +} + +// EncryptMessage encrypt message with public key, if pass private key and passphrase will also sign the message +// publicKey : string armored public key +// plainText : the input +// privateKey : optional required when you want to sign +// passphrase : optional required when you pass the private key and this passphrase must could decrypt the private key +func (o *OpenPGP) EncryptMessage(plainText string, publicKey string, privateKey string, passphrase string, trim bool) (string, error) { + rawPubKey, err := UnArmor(publicKey) + if err != nil { + return "", err + } + return o.EncryptMessageBinKey(plainText, rawPubKey, privateKey, passphrase, trim) +} + +// EncryptMessageBinKey encrypt message with public key, if pass private key and passphrase will also sign the message +// publicKey : bytes unarmored public key +// plainText : the input +// privateKey : optional required when you want to sign +// passphrase : optional required when you pass the private key and this passphrase must could decrypt the private key +func (o *OpenPGP) EncryptMessageBinKey(plainText string, publicKey []byte, privateKey string, passphrase string, trim bool) (string, error) { + + var outBuf bytes.Buffer + w, err := armor.Encode(&outBuf, pgpMessageType.string(), armorHeader) + if err != nil { + return "", err + } + + pubKeyReader := bytes.NewReader(publicKey) + pubKeyEntries, err := openpgp.ReadKeyRing(pubKeyReader) + if err != nil { + return "", err + } + + var signEntity *openpgp.Entity + + if len(passphrase) > 0 && len(privateKey) > 0 { + signerReader := strings.NewReader(privateKey) + signerEntries, err := openpgp.ReadArmoredKeyRing(signerReader) + if err != nil { + return "", err + } + + for _, e := range signerEntries { + // Entity.PrivateKey must be a signing key + if e.PrivateKey != nil { + if e.PrivateKey.Encrypted { + e.PrivateKey.Decrypt([]byte(passphrase)) + } + if !e.PrivateKey.Encrypted { + signEntity = e + break + } + } + } + + if signEntity == nil { + return "", errors.New("cannot sign message, singer key is not unlocked") + } + } + + config := &packet.Config{DefaultCipher: packet.CipherAES256} + + ew, err := openpgp.Encrypt(w, pubKeyEntries, signEntity, nil, config) + + _, _ = ew.Write([]byte(plainText)) + ew.Close() + w.Close() + return outBuf.String(), nil +} + +//EncryptMessageWithPassword ... +func (o *OpenPGP) EncryptMessageWithPassword(plainText string, password string) (string, error) { + + var outBuf bytes.Buffer + w, err := armor.Encode(&outBuf, pgpMessageType.string(), armorHeader) + if err != nil { + return "", err + } + + plaintext, err := openpgp.SymmetricallyEncrypt(w, []byte(password), nil, nil) + if err != nil { + return "", err + } + message := []byte(plainText) + _, err = plaintext.Write(message) + if err != nil { + return "", err + } + err = plaintext.Close() + if err != nil { + return "", err + } + w.Close() + + return outBuf.String(), nil +} + +//DecryptMessageWithPassword ... +func (o *OpenPGP) DecryptMessageWithPassword(encrypted string, password string) (string, error) { + + encryptedio, err := unArmor(encrypted) + if err != nil { + return "", err + } + + var prompt = func(keys []openpgp.Key, symmetric bool) ([]byte, error) { + return []byte(password), nil + } + + md, err := openpgp.ReadMessage(encryptedio.Body, nil, prompt, nil) + if err != nil { + return "", err + } + + messageBuf := bytes.NewBuffer(nil) + _, err = io.Copy(messageBuf, md.UnverifiedBody) + if err != nil { + return "", err + } + + return messageBuf.String(), nil +} diff --git a/notes.txt b/notes.txt new file mode 100644 index 0000000..e69de29 diff --git a/openpgp.go b/openpgp.go new file mode 100644 index 0000000..7502659 --- /dev/null +++ b/openpgp.go @@ -0,0 +1,38 @@ +package pm + +// OpenPGP strutature to manager mutiple address keys and user keys +type OpenPGP struct { + // key ring not in used + addresses []*Address + + //lastestServerTime unix time cache + lastestServerTime int64 +} + +// //AddAddress add a new address to key ring +// //add a new address into addresses list +// func (pgp *OpenPGP) 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 *OpenPGP) 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 *OpenPGP) CleanAddresses() (bool, error) { +// return true, errors.New("this is not implemented yet, will add this later") +// } + +// //EncryptMessage encrypt message use address id +// func (pgp *OpenPGP) 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 *OpenPGP) DecryptMessage(encryptText string, passphras string) (string, error) { +// return "", errors.New("this is not implemented yet, will add this later") +// } diff --git a/readme.mk b/readme.mk new file mode 100644 index 0000000..a2549b3 --- /dev/null +++ b/readme.mk @@ -0,0 +1,52 @@ +setup gomobile and build/bind the source code: + +Gomobile repo: https://github.com/golang/mobile +Gomobile wiki: https://github.com/golang/go/wiki/Mobile +ProtonMail Openpgp: https://github.com/ProtonMail/crypto/tree/master/openpgp + +1. Install Go: brew install go +2. Install Gomobile: go get golang.org/x/mobile/cmd/gomobile + //go get -u golang.org/x/mobile/cmd/... +3. Install Gobind: go install golang.org/x/mobile/cmd/gobind +3. Install android sdk and ndk use android studio +4. Set Env: export ANDROID_HOME="/AndroidSDK" #set your own path +5. Init gomobile: gomobile init -ndk /AndroidSDK/ndk-bundle/ #put your own ndk path + +6. build examples: + gomobile build -target=android #or ios + + bind examples: + gomobile bind -target ios -o frameworks/name.framework + gomobile bind -target android + +the bind will create framework for ios and jar&aar file for android x86_64 arm arch + + +7. Project uses glide to setup vendor + +OTHER NOTES: +two way bridge go & swift: +https://medium.com/@matryer/tutorial-calling-go-code-from-swift-on-ios-and-vice-versa-with-gomobile-7925620c17a4 + + +SOME UNSOLVED ISSUES: +No Mips support but this is fine we don't support it anyway +https://github.com/golang/go/issues/23692 issue with atomic +EXC_BAD_ACCESS is hard to catch +https://github.com/golang/go/issues/21288 memory issue +https://github.com/golang/go/issues/21594 gradle issue +https://github.com/golang/go/issues/23307 +https://github.com/golang/go/issues/20241 + upload failed. we are using framework need to confirm this +https://github.com/golang/go/issues/17278 android load jni issue +https://github.com/golang/go/issues/17807 dlopen issue +https://github.com/golang/go/issues/18903 doesn't work well with vender +https://github.com/golang/go/issues/13438 bind issue +https://github.com/golang/go/issues/14332 build types issue +https://github.com/golang/go/issues/15956 no multiple independent bindings support + + + + + +The build.sh need to modify the path when you use it \ No newline at end of file diff --git a/session.go b/session.go new file mode 100644 index 0000000..846922d --- /dev/null +++ b/session.go @@ -0,0 +1,364 @@ +package pm + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "strings" + + "golang.org/x/crypto/openpgp" + "golang.org/x/crypto/openpgp/packet" +) + +//RandomToken ... +func RandomToken() ([]byte, error) { + config := &packet.Config{DefaultCipher: packet.CipherAES256} + keySize := config.DefaultCipher.KeySize() + symKey := make([]byte, keySize) + if _, err := io.ReadFull(config.Random(), symKey); err != nil { + return nil, err + } + return symKey, nil +} + +// RandomTokenWith ... +func RandomTokenWith(size int) ([]byte, error) { + config := &packet.Config{DefaultCipher: packet.CipherAES256} + symKey := make([]byte, size) + if _, err := io.ReadFull(config.Random(), symKey); err != nil { + return nil, err + } + return symKey, nil +} + +//GetSessionFromKeyPacketBinkeys get session key no encoding in and out +func GetSessionFromKeyPacketBinkeys(keyPackage []byte, privateKey []byte, passphrase string) (*SessionSplit, error) { + + keyReader := bytes.NewReader(keyPackage) + packets := packet.NewReader(keyReader) + + var p packet.Packet + var err error + if p, err = packets.Next(); err != nil { + return nil, err + } + + ek := p.(*packet.EncryptedKey) + + privKey := bytes.NewReader(privateKey) + privKeyEntries, err := openpgp.ReadKeyRing(privKey) + if err != nil { + return nil, err + } + rawPwd := []byte(passphrase) + var decryptErr error + for _, key := range privKeyEntries.DecryptionKeys() { + priv := key.PrivateKey + if priv.Encrypted { + if err := priv.Decrypt(rawPwd); err != nil { + continue + } + } + + if decryptErr = ek.Decrypt(priv, nil); decryptErr == nil { + break + } + } + + if decryptErr != nil { + return nil, err + } + + return getSessionSplit(ek) +} + +//GetSessionFromKeyPacket get session key no encoding in and out +func GetSessionFromKeyPacket(keyPackage []byte, privateKey string, passphrase string) (*SessionSplit, error) { + + keyReader := bytes.NewReader(keyPackage) + packets := packet.NewReader(keyReader) + + var p packet.Packet + var err error + if p, err = packets.Next(); err != nil { + return nil, err + } + + ek := p.(*packet.EncryptedKey) + + privKey := strings.NewReader(privateKey) + privKeyEntries, err := openpgp.ReadArmoredKeyRing(privKey) + if err != nil { + return nil, err + } + rawPwd := []byte(passphrase) + var decryptErr error + for _, key := range privKeyEntries.DecryptionKeys() { + priv := key.PrivateKey + if priv.Encrypted { + if err := priv.Decrypt(rawPwd); err != nil { + continue + } + } + + if decryptErr = ek.Decrypt(priv, nil); decryptErr == nil { + break + } + } + + if decryptErr != nil { + return nil, err + } + + return getSessionSplit(ek) +} + +//KeyPacketWithPublicKey ... +func KeyPacketWithPublicKey(sessionSplit *SessionSplit, publicKey string) ([]byte, error) { + pubkeyRaw, err := UnArmor(publicKey) + if err != nil { + return nil, err + } + return KeyPacketWithPublicKeyBin(sessionSplit, pubkeyRaw) +} + +// KeyPacketWithPublicKeyBin ... +func KeyPacketWithPublicKeyBin(sessionSplit *SessionSplit, publicKey []byte) ([]byte, error) { + publicKeyReader := bytes.NewReader(publicKey) + pubKeyEntries, err := openpgp.ReadKeyRing(publicKeyReader) + + outbuf := &bytes.Buffer{} + + cf := cipherFunc(sessionSplit.Algo) + + if len(pubKeyEntries) == 0 { + return nil, errors.New("cannot set key: key ring is empty") + } + + var pub *packet.PublicKey + for _, e := range pubKeyEntries { + 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 { + return nil, errors.New("cannot set key: no public key available") + } + + if err = packet.SerializeEncryptedKey(outbuf, pub, cf, sessionSplit.Session, nil); err != nil { + err = fmt.Errorf("pmapi: cannot set key: %v", err) + return nil, errors.New("cannot set key: key ring is empty") + } + return outbuf.Bytes(), nil +} + +//GetSessionFromSymmetricPacket ... +func GetSessionFromSymmetricPacket(keyPackage []byte, password string) (*SessionSplit, error) { + + keyReader := bytes.NewReader(keyPackage) + packets := packet.NewReader(keyReader) + + var symKeys []*packet.SymmetricKeyEncrypted + for { + + var p packet.Packet + var err error + if p, err = packets.Next(); err != nil { + break + } + + switch p := p.(type) { + case *packet.SymmetricKeyEncrypted: + symKeys = append(symKeys, p) + } + } + + pwdRaw := []byte(password) + // Try the symmetric passphrase first + if len(symKeys) != 0 && pwdRaw != nil { + for _, s := range symKeys { + key, cipherFunc, err := s.Decrypt(pwdRaw) + if err == nil { + return &SessionSplit{ + Session: key, + Algo: getAlog(cipherFunc), + }, nil + } + + } + } + + return nil, errors.New("password incorrect") +} + +// SymmetricKeyPacketWithPassword ... +func SymmetricKeyPacketWithPassword(sessionSplit *SessionSplit, password string) ([]byte, error) { + outbuf := &bytes.Buffer{} + + cf := cipherFunc(sessionSplit.Algo) + + if len(password) <= 0 { + return nil, errors.New("password can't be empty") + } + + pwdRaw := []byte(password) + + config := &packet.Config{ + DefaultCipher: cf, + } + + err := packet.SerializeSymmetricKeyEncryptedReuseKey(outbuf, sessionSplit.Session, 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 this's cipher function. +func cipherFunc(algo string) packet.CipherFunction { + cf, ok := symKeyAlgos[algo] + if ok { + return cf + } + return packet.CipherAES256 +} + +func getSessionSplit(ek *packet.EncryptedKey) (*SessionSplit, error) { + if ek == nil { + return nil, errors.New("can't decrypt key packet") + } + var algo string + for k, v := range symKeyAlgos { + if v == ek.CipherFunc { + algo = k + break + } + } + if algo == "" { + algo = "aes256" + } + + return &SessionSplit{ + Session: ek.Key, + Algo: algo, + }, nil +} + +func getAlog(cipher packet.CipherFunction) string { + var algo string + for k, v := range symKeyAlgos { + if v == cipher { + algo = k + break + } + } + if algo == "" { + algo = "aes256" + } + + return algo +} + +//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 +} + +//SeparateKeyAndData ... +func SeparateKeyAndData(encrypted string) (*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) + + outSplt := &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...) + + outSplt.DataPacket = symEncryptedData + break + + } + } + + var buff bytes.Buffer + ek.Serialize(&buff) + outSplt.KeyPacket = buff.Bytes() + + return outSplt, err +} diff --git a/sign_detached.go b/sign_detached.go new file mode 100644 index 0000000..57f37d3 --- /dev/null +++ b/sign_detached.go @@ -0,0 +1,322 @@ +package pm + +import ( + "bytes" + "errors" + "strings" + "time" + + "golang.org/x/crypto/openpgp" + "golang.org/x/crypto/openpgp/clearsign" + "golang.org/x/crypto/openpgp/packet" +) + +//ReadClearSignedMessage read clear message from a clearsign package +func ReadClearSignedMessage(signedMessage string) (string, error) { + modulusBlock, rest := clearsign.Decode([]byte(signedMessage)) + if len(rest) != 0 { + return "", errors.New("pmapi: extra data after modulus") + } + return string(modulusBlock.Bytes), nil +} + +// SignTextDetached sign detached text type +func (o *OpenPGP) SignTextDetached(plainText string, privateKey string, passphrase string, trim bool) (string, error) { + //sign with 0x01 text + var signEntity *openpgp.Entity + + signerReader := strings.NewReader(privateKey) + signerEntries, err := openpgp.ReadArmoredKeyRing(signerReader) + if err != nil { + return "", err + } + + for _, e := range signerEntries { + // Entity.PrivateKey must be a signing key + if e.PrivateKey != nil { + if e.PrivateKey.Encrypted { + e.PrivateKey.Decrypt([]byte(passphrase)) + } + if !e.PrivateKey.Encrypted { + signEntity = e + break + } + } + } + + if signEntity == nil { + return "", errors.New("cannot sign message, singer key is not unlocked") + } + + config := &packet.Config{DefaultCipher: packet.CipherAES256} + + att := strings.NewReader(plainText) + + var outBuf bytes.Buffer + //SignText + if err = openpgp.ArmoredDetachSignText(&outBuf, signEntity, att, config); err != nil { + return "", err + } + + return outBuf.String(), nil +} + +// SignTextDetachedBinKey ... +func (o *OpenPGP) SignTextDetachedBinKey(plainText string, privateKey []byte, passphrase string, trim bool) (string, error) { + //sign with 0x01 + var signEntity *openpgp.Entity + + signerReader := bytes.NewReader(privateKey) + signerEntries, err := openpgp.ReadKeyRing(signerReader) + if err != nil { + return "", err + } + + for _, e := range signerEntries { + // Entity.PrivateKey must be a signing key + if e.PrivateKey != nil { + if e.PrivateKey.Encrypted { + e.PrivateKey.Decrypt([]byte(passphrase)) + } + if !e.PrivateKey.Encrypted { + signEntity = e + break + } + } + } + + if signEntity == nil { + return "", errors.New("cannot sign message, singer key is not unlocked") + } + + config := &packet.Config{DefaultCipher: packet.CipherAES256} + + att := strings.NewReader(plainText) + + var outBuf bytes.Buffer + //sign text + if err = openpgp.ArmoredDetachSignText(&outBuf, signEntity, att, config); err != nil { + return "", err + } + + return outBuf.String(), nil +} + +// SignBinDetached sign bin data +func (o *OpenPGP) SignBinDetached(plainData []byte, privateKey string, passphrase string) (string, error) { + //sign with 0x00 + var signEntity *openpgp.Entity + + signerReader := strings.NewReader(privateKey) + signerEntries, err := openpgp.ReadArmoredKeyRing(signerReader) + if err != nil { + return "", err + } + + for _, e := range signerEntries { + // Entity.PrivateKey must be a signing key + if e.PrivateKey != nil { + if e.PrivateKey.Encrypted { + e.PrivateKey.Decrypt([]byte(passphrase)) + } + if !e.PrivateKey.Encrypted { + signEntity = e + break + } + } + } + + if signEntity == nil { + return "", errors.New("cannot sign message, singer key is not unlocked") + } + + config := &packet.Config{DefaultCipher: packet.CipherAES256} + + att := bytes.NewReader(plainData) + + var outBuf bytes.Buffer + //sign bin + if err = openpgp.ArmoredDetachSign(&outBuf, signEntity, att, config); err != nil { + return "", err + } + + return outBuf.String(), nil +} + +// SignBinDetachedBinKey ... +func (o *OpenPGP) SignBinDetachedBinKey(plainData []byte, privateKey []byte, passphrase string) (string, error) { + //sign with 0x00 + var signEntity *openpgp.Entity + + signerReader := bytes.NewReader(privateKey) + signerEntries, err := openpgp.ReadKeyRing(signerReader) + if err != nil { + return "", err + } + + for _, e := range signerEntries { + // Entity.PrivateKey must be a signing key + if e.PrivateKey != nil { + if e.PrivateKey.Encrypted { + e.PrivateKey.Decrypt([]byte(passphrase)) + } + if !e.PrivateKey.Encrypted { + signEntity = e + break + } + } + } + + if signEntity == nil { + return "", errors.New("cannot sign message, singer key is not unlocked") + } + + config := &packet.Config{DefaultCipher: packet.CipherAES256} + + att := bytes.NewReader(plainData) + + var outBuf bytes.Buffer + //sign bin + if err = openpgp.ArmoredDetachSign(&outBuf, signEntity, att, config); err != nil { + return "", err + } + + return outBuf.String(), nil +} + +// VerifyTextSignDetached ... +func (o *OpenPGP) VerifyTextSignDetached(signature string, plainText string, publicKey string, verifyTime int64) (bool, error) { + + pubKeyReader := strings.NewReader(publicKey) + + pubKeyEntries, err := openpgp.ReadArmoredKeyRing(pubKeyReader) + if err != nil { + return false, err + } + + signatureReader := strings.NewReader(signature) + + origText := bytes.NewReader(bytes.NewBufferString(plainText).Bytes()) + + config := &packet.Config{} + if verifyTime > 0 { + tm := time.Unix(verifyTime, 0) + config.Time = func() time.Time { + return tm + } + } + signer, err := openpgp.CheckArmoredDetachedSignature(pubKeyEntries, origText, signatureReader, config) + if err != nil { + return false, err + } + if signer == nil { + return false, errors.New("signer is empty") + } + // if signer.PrimaryKey.KeyId != signed.PrimaryKey.KeyId { + // // t.Errorf("wrong signer got:%x want:%x", signer.PrimaryKey.KeyId, 0) + // return false, errors.New("signer is nil") + // } + return true, nil +} + +// VerifyTextSignDetachedBinKey ... +func (o *OpenPGP) VerifyTextSignDetachedBinKey(signature string, plainText string, publicKey []byte, verifyTime int64) (bool, error) { + + pubKeyReader := bytes.NewReader(publicKey) + + pubKeyEntries, err := openpgp.ReadKeyRing(pubKeyReader) + if err != nil { + return false, err + } + + signatureReader := strings.NewReader(signature) + + origText := bytes.NewReader(bytes.NewBufferString(plainText).Bytes()) + config := &packet.Config{} + if verifyTime > 0 { + tm := time.Unix(verifyTime, 0) + config.Time = func() time.Time { + return tm + } + } + signer, err := openpgp.CheckArmoredDetachedSignature(pubKeyEntries, origText, signatureReader, config) + if err != nil { + return false, err + } + if signer == nil { + return false, errors.New("signer is empty") + } + // if signer.PrimaryKey.KeyId != signed.PrimaryKey.KeyId { + // // t.Errorf("wrong signer got:%x want:%x", signer.PrimaryKey.KeyId, 0) + // return false, errors.New("signer is nil") + // } + return true, nil +} + +// VerifyBinSignDetached ... +func (o *OpenPGP) VerifyBinSignDetached(signature string, plainData []byte, publicKey string, verifyTime int64) (bool, error) { + + pubKeyReader := strings.NewReader(publicKey) + + pubKeyEntries, err := openpgp.ReadArmoredKeyRing(pubKeyReader) + if err != nil { + return false, err + } + + signatureReader := strings.NewReader(signature) + + origText := bytes.NewReader(plainData) + config := &packet.Config{} + if verifyTime > 0 { + tm := time.Unix(verifyTime, 0) + config.Time = func() time.Time { + return tm + } + } + signer, err := openpgp.CheckArmoredDetachedSignature(pubKeyEntries, origText, signatureReader, config) + if err != nil { + return false, err + } + if signer == nil { + return false, errors.New("signer is empty") + } + // if signer.PrimaryKey.KeyId != signed.PrimaryKey.KeyId { + // // t.Errorf("wrong signer got:%x want:%x", signer.PrimaryKey.KeyId, 0) + // return false, errors.New("signer is nil") + // } + return true, nil +} + +// VerifyBinSignDetachedBinKey ... +func (o *OpenPGP) VerifyBinSignDetachedBinKey(signature string, plainData []byte, publicKey []byte, verifyTime int64) (bool, error) { + pubKeyReader := bytes.NewReader(publicKey) + + pubKeyEntries, err := openpgp.ReadKeyRing(pubKeyReader) + if err != nil { + return false, err + } + + signatureReader := strings.NewReader(signature) + + origText := bytes.NewReader(plainData) + + config := &packet.Config{} + if verifyTime > 0 { + tm := time.Unix(verifyTime, 0) + config.Time = func() time.Time { + return tm + } + } + signer, err := openpgp.CheckArmoredDetachedSignature(pubKeyEntries, origText, signatureReader, config) + if err != nil { + return false, err + } + if signer == nil { + return false, errors.New("signer is empty") + } + // if signer.PrimaryKey.KeyId != signed.PrimaryKey.KeyId { + // // t.Errorf("wrong signer got:%x want:%x", signer.PrimaryKey.KeyId, 0) + // return false, errors.New("signer is nil") + // } + return true, nil +} diff --git a/time.go b/time.go new file mode 100644 index 0000000..17dd0af --- /dev/null +++ b/time.go @@ -0,0 +1,25 @@ +package pm + +import ( + "time" +) + +// UpdateTime update cached time +func (o *OpenPGP) UpdateTime(newTime int64) { + o.lastestServerTime = newTime +} + +//GetTime get latest cached time +func (o *OpenPGP) GetTime() int64 { + return o.lastestServerTime +} + +func (o *OpenPGP) getNow() time.Time { + + if o.lastestServerTime > 0 { + tm := time.Unix(o.lastestServerTime, 0) + return tm + } + + return time.Now() +} diff --git a/version.go b/version.go new file mode 100644 index 0000000..c8cbd8b --- /dev/null +++ b/version.go @@ -0,0 +1,6 @@ +package pm + +// Version get current lib version +func Version() string { + return "ddacebe0" +}