encryptor/encryptor.go

385 lines
8.2 KiB
Go
Executable File

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)
}