From 8d300e4782cd41a97e7dd6f55719466247f7a710 Mon Sep 17 00:00:00 2001 From: zhj4478 Date: Mon, 4 Jun 2018 16:05:14 -0700 Subject: [PATCH] wrapper for mobile --- .gitignore | 4 + armor.go | 61 ++++++++ attachment.go | 158 ++++++++++++++++++++ build.sh | 27 ++++ common.go | 23 +++ fingerprint.go | 32 +++++ glide.lock | 26 ++++ glide.yaml | 5 + key.go | 298 ++++++++++++++++++++++++++++++++++++++ message.go | 322 +++++++++++++++++++++++++++++++++++++++++ notes.txt | 0 openpgp.go | 38 +++++ readme.mk | 52 +++++++ session.go | 364 +++++++++++++++++++++++++++++++++++++++++++++++ sign_detached.go | 322 +++++++++++++++++++++++++++++++++++++++++ time.go | 25 ++++ version.go | 6 + 17 files changed, 1763 insertions(+) create mode 100644 .gitignore create mode 100644 armor.go create mode 100644 attachment.go create mode 100755 build.sh create mode 100644 common.go create mode 100644 fingerprint.go create mode 100644 glide.lock create mode 100644 glide.yaml create mode 100644 key.go create mode 100644 message.go create mode 100644 notes.txt create mode 100644 openpgp.go create mode 100644 readme.mk create mode 100644 session.go create mode 100644 sign_detached.go create mode 100644 time.go create mode 100644 version.go 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" +}