package encryptor import ( "bytes" "crypto" "crypto/aes" "crypto/cipher" "crypto/rand" "encoding/base64" "errors" "io" "io/ioutil" "os" "path" "strings" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/packet" // for openpgp _ "golang.org/x/crypto/ripemd160" ) const keylen = 32 // Encryptor interface type Encryptor interface { Encrypt(string) string Decrypt(string) string SetPublic(string) GetPublic() string SetSymmetric(string) FromFiles(string, string) error ToFiles(string, string) error } type encryptor struct { entity *openpgp.Entity writing *openpgp.Entity symmetric string b64 bool } func (e *encryptor) SetPublic(pub string) { reader := b64dec(pub) e.writing = entityFromPackets(nil, strToPubPack(reader)) } func (e *encryptor) GetPublic() string { bufpub := new(bytes.Buffer) e.writing.Serialize(bufpub) bufPu, err := ioutil.ReadAll(bufpub) if err != nil { panic(err) } return b64enc(string(bufPu)) } func (e *encryptor) setPrivate(pri string) { readerPri := b64dec(pri) readerPub := b64dec(e.GetPublic()) e.entity = entityFromPackets(strToPriPack(readerPri), strToPubPack(readerPub)) } func (e *encryptor) getPrivate() string { bufpri := new(bytes.Buffer) e.entity.SerializePrivate(bufpri, nil) bufPr, err := ioutil.ReadAll(bufpri) if err != nil { panic(err) } return b64enc(string(bufPr)) } // NewKeyPair generates a PGP private-public key pair. func NewKeyPair() (string, string) { entity, err := openpgp.NewEntity("", "", "", nil) if err != nil { panic(err) } en := &encryptor{ entity: entity, writing: entity, } return en.getPrivate(), en.GetPublic() } func (e *encryptor) getPubPacket() *packet.PublicKey { return e.writing.PrimaryKey } func (e *encryptor) getPriPacket() *packet.PrivateKey { return e.entity.PrivateKey } func entityFromPackets(priKey *packet.PrivateKey, pubKey *packet.PublicKey) *openpgp.Entity { config := packet.Config{ DefaultHash: crypto.SHA256, DefaultCipher: packet.CipherAES256, DefaultCompressionAlgo: packet.CompressionZLIB, CompressionConfig: &packet.CompressionConfig{ Level: 9, }, RSABits: 4096, } currentTime := config.Now() uid := packet.NewUserId("", "", "") e := openpgp.Entity{ PrimaryKey: pubKey, PrivateKey: priKey, Identities: make(map[string]*openpgp.Identity), } isPrimaryID := false e.Identities[uid.Id] = &openpgp.Identity{ Name: uid.Name, UserId: uid, SelfSignature: &packet.Signature{ CreationTime: currentTime, SigType: packet.SigTypePositiveCert, PubKeyAlgo: packet.PubKeyAlgoRSA, Hash: config.Hash(), IsPrimaryId: &isPrimaryID, FlagsValid: true, FlagSign: true, FlagCertify: true, IssuerKeyId: &e.PrimaryKey.KeyId, }, } keyLifetimeSecs := uint32(86400 * 365) e.Subkeys = make([]openpgp.Subkey, 1) e.Subkeys[0] = openpgp.Subkey{ PublicKey: pubKey, PrivateKey: priKey, Sig: &packet.Signature{ CreationTime: currentTime, SigType: packet.SigTypeSubkeyBinding, PubKeyAlgo: packet.PubKeyAlgoRSA, Hash: config.Hash(), PreferredHash: []uint8{8}, // SHA-256 FlagsValid: true, FlagEncryptStorage: true, FlagEncryptCommunications: true, IssuerKeyId: &e.PrimaryKey.KeyId, KeyLifetimeSecs: &keyLifetimeSecs, }, } return &e } func strToPack(str string) interface{} { reader := b64dec(str) KeyRead := bytes.NewReader([]byte(reader)) PacketReader := packet.NewReader(KeyRead) pkt, err := PacketReader.Next() if err != nil { panic(err) } return pkt } func strToPriPack(private string) *packet.PrivateKey { priKeyPack, ok := strToPack(private).(*packet.PrivateKey) if !ok { panic(ok) } return priKeyPack } func strToPubPack(public string) *packet.PublicKey { pubKeyPack, ok := strToPack(public).(*packet.PublicKey) if !ok { panic(ok) } return pubKeyPack } // NewEncryptor function func NewEncryptor(private, public string, b64 ...bool) *encryptor { backupPri, backupPub := NewKeyPair() if private == "" { private = backupPri } if public == "" { public = backupPub } priKeyPack := strToPriPack(private) pubKeyPack := strToPubPack(public) b64payloads := len(b64) > 0 && b64[0] return &encryptor{ entity: entityFromPackets(priKeyPack, pubKeyPack), writing: entityFromPackets(nil, pubKeyPack), b64: b64payloads, } } func b64enc(msg string) string { return base64.StdEncoding.EncodeToString([]byte(msg)) } func b64dec(msg string) string { decMe, err := base64.StdEncoding.DecodeString(msg) if err != nil { return msg } return string(decMe) } func (e *encryptor) SetSymmetric(key string) { e.symmetric = key } func (e *encryptor) Encrypt(msg string) string { if e.symmetric != "" { return e.symEncrypt(msg) } return e.asymEncrypt(msg) } func (e *encryptor) Decrypt(msg string) string { if e.symmetric != "" { return e.symDecrypt(msg) } return e.asymDecrypt(msg) } func fixKey(key string) []byte { return []byte(strings.Repeat("minkey"+string(key), keylen)[:keylen]) } func pad(src []byte) []byte { padding := aes.BlockSize - len(src)%aes.BlockSize padtext := bytes.Repeat([]byte{byte(padding)}, padding) return append(src, padtext...) } func unpad(src []byte) ([]byte, error) { length := len(src) unpadding := int(src[length-1]) if unpadding > length { return nil, errors.New("unpad error. This could happen when incorrect encryption key is used") } return src[:(length - unpadding)], nil } func (e *encryptor) symEncrypt(smsg string) string { key := fixKey(e.symmetric) block, err := aes.NewCipher(key) if err != nil { return "" } msg := pad([]byte(smsg)) ciphertext := make([]byte, aes.BlockSize+len(msg)) iv := ciphertext[:aes.BlockSize] if _, err := io.ReadFull(rand.Reader, iv); err != nil { return "" } cfb := cipher.NewCFBEncrypter(block, iv) cfb.XORKeyStream(ciphertext[aes.BlockSize:], msg) if e.b64 { return b64enc(string(ciphertext)) } return string(ciphertext) } func (e *encryptor) symDecrypt(msg string) string { payload := []byte(msg) if e.b64 { payload = []byte(b64dec(msg)) } if len(payload) == 0 { return "" } key := fixKey(e.symmetric) block, err := aes.NewCipher(key) if err != nil { return "" } if (len(payload) % aes.BlockSize) != 0 { return "" } iv := payload[:aes.BlockSize] bmsg := payload[aes.BlockSize:] cfb := cipher.NewCFBDecrypter(block, iv) cfb.XORKeyStream(bmsg, bmsg) b, _ := unpad(bmsg) return string(b) } // Encrypt function func (e *encryptor) asymEncrypt(msg string) string { // encrypt string buf := new(bytes.Buffer) w, err := openpgp.Encrypt(buf, []*openpgp.Entity{e.writing}, nil, nil, nil) if err != nil { return "" } _, err = w.Write([]byte(msg)) if err != nil { return "" } err = w.Close() if err != nil { return "" } // Encode to b64 return b64enc(string(buf.Bytes())) } // Decrypt function func (e *encryptor) asymDecrypt(msg string) string { // Decode the b64 string decMe := b64dec(msg) KeyRead := bytes.NewReader([]byte(decMe)) md, err := openpgp.ReadMessage(KeyRead, openpgp.EntityList([]*openpgp.Entity{e.entity}), nil, nil) if err != nil { return "" } plaintext, err := ioutil.ReadAll(md.UnverifiedBody) if err != nil { return "" } return string(plaintext) } func (e *encryptor) FromFiles(privatePath, publicPath string) error { priRaw, err := ioutil.ReadFile(privatePath) if err != nil { return err } pubRaw, err := ioutil.ReadFile(publicPath) if err != nil { return err } d := NewEncryptor(string(priRaw), string(pubRaw)) v := d.Encrypt("Hello") if len(v) == 0 { return errors.New("bad public-private key file pair") } v = d.Decrypt(v) if len(v) == 0 { return errors.New("bad public-private key file pair") } e.entity = d.entity e.writing = d.entity return nil } func (e *encryptor) ToFiles(privatePath, publicPath string) error { d := &encryptor{ writing: e.entity, entity: e.entity, } if err := os.MkdirAll(path.Dir(privatePath), os.ModePerm); err != nil { return err } if err := ioutil.WriteFile(privatePath, []byte(d.getPrivate()), os.ModePerm); err != nil { return err } if err := os.MkdirAll(path.Dir(publicPath), os.ModePerm); err != nil { return err } return ioutil.WriteFile(publicPath, []byte(d.GetPublic()), os.ModePerm) }