commit f1b1368afd77c480470ef15e5d39fb6785ebfec1 Author: Tovi Jaeschke-Rogers Date: Mon Jun 1 12:26:45 2020 +0930 Initial commit for file encryption tool diff --git a/Encryption/CreateCipherBlock.go b/Encryption/CreateCipherBlock.go new file mode 100644 index 0000000..a5508b2 --- /dev/null +++ b/Encryption/CreateCipherBlock.go @@ -0,0 +1,28 @@ +package Encryption + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/sha256" + "hash" +) + +func CreateHash(key string) []byte { + var h hash.Hash + h = hmac.New(sha256.New, []byte(key)) + h.Write([]byte(key)) + return h.Sum(nil) +} + +func CreateKey(hashedKey []byte) (cipher.Block, error) { + var ( + block cipher.Block + e error + ) + block, e = aes.NewCipher(hashedKey) + if e != nil { + return nil, e + } + return block, nil +} diff --git a/Encryption/Decrypt.go b/Encryption/Decrypt.go new file mode 100644 index 0000000..06dd4dc --- /dev/null +++ b/Encryption/Decrypt.go @@ -0,0 +1,101 @@ +package Encryption + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "errors" + "io" + "io/ioutil" + "os" +) + +func DecryptData(password string, ciphertext []byte) ([]byte, error) { + var ( + hashedKey []byte + iv []byte + encKey []byte + block cipher.Block + stream cipher.Stream + e error + ) + hashedKey = CreateHash(password) + if len(ciphertext) == 0 { + return []byte{}, errors.New("Invalid length") + } + iv = ciphertext[:aes.BlockSize] + encKey = ciphertext[:32+aes.BlockSize][aes.BlockSize:] + ciphertext = ciphertext[aes.BlockSize+32:] + + block, e = CreateKey(hashedKey) + if e != nil { + return []byte{}, e + } + + stream = cipher.NewCFBDecrypter(block, iv) + stream.XORKeyStream(encKey, encKey) + + for i := range encKey { + if encKey[i] != hashedKey[i] { + return []byte{}, errors.New("Incorrect Password") + } + } + + stream.XORKeyStream(ciphertext, ciphertext) + return ciphertext, nil +} + +func DecryptFile(password string, FilePath string) error { + var ( + OldFilePath string + ciphertext []byte + plaintext []byte + plaintextFile *os.File + e error + ) + OldFilePath = FilePath[:(len(FilePath) - 4)] + ciphertext, e = ioutil.ReadFile(FilePath) + if e != nil { + return e + } + if len(ciphertext) < aes.BlockSize { + return errors.New("ciphertext too short") + } + + plaintext, e = DecryptData(password, ciphertext) + if e != nil { + return e + } + + plaintextFile, e = os.Create(OldFilePath) + if e != nil { + return e + } + _, e = io.Copy(plaintextFile, bytes.NewReader(plaintext)) + if e != nil { + return e + } + os.Remove(FilePath) + return nil +} + +func DecryptAndReadFile(password string, FilePath string) (string, error) { + var ( + ciphertext []byte + plaintext []byte + e error + ) + ciphertext, e = ioutil.ReadFile(FilePath) + if e != nil { + return "", e + } + if len(ciphertext) < aes.BlockSize { + return "", errors.New("ciphertext too short") + } + + plaintext, e = DecryptData(password, ciphertext) + if e != nil { + return "", e + } + return string(plaintext), nil +} diff --git a/Encryption/Edit.go b/Encryption/Edit.go new file mode 100644 index 0000000..e072c08 --- /dev/null +++ b/Encryption/Edit.go @@ -0,0 +1,95 @@ +package Encryption + +import ( + "fmt" + "bytes" + "crypto/aes" + "errors" + "os" + "os/exec" + "io/ioutil" + "io" +) + +func EditEncryptedFile(password string, FilePath string) (error) { + var ( + editor string + tmpFilePath string + ciphertext []byte + plaintext []byte + tmpFile *os.File + encryptedFile *os.File + cmd *exec.Cmd + e error + ) + editor = os.Getenv("EDITOR") + if editor == "" { + return errors.New("EDITOR variable cannot be blank") + } + + tmpFilePath = "/tmp/klsadjhflk" + ciphertext, e = ioutil.ReadFile(FilePath) + if e != nil { + return e + } + if len(ciphertext) < aes.BlockSize { + return errors.New("ciphertext too short") + } + + plaintext, e = DecryptData(password, ciphertext) + if e != nil { + return e + } + + tmpFile, e = os.Create(tmpFilePath) + if e != nil { + return e + } + _, e = io.Copy(tmpFile, bytes.NewReader(plaintext)) + if e != nil { + return e + } + + e = tmpFile.Close() + if e != nil { + return e + } + + cmd = exec.Command(editor, tmpFilePath) + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + cmd.Stderr = os.Stderr + e = cmd.Run() + if (e != nil) { + return e + } + + plaintext, e = ioutil.ReadFile(tmpFilePath) + if e != nil { + return e + } + + ciphertext, e = EncryptData(password, plaintext) + if e != nil { + return e + } + // open output file + encryptedFile, e = os.OpenFile(FilePath, os.O_RDWR, 0666) + if e != nil { + fmt.Println(1) + return e + } + + defer func() { + encryptedFile.Close() + SecureDelete(tmpFilePath) + }() + + _, e = io.Copy(encryptedFile, bytes.NewReader(ciphertext)) + if e != nil { + fmt.Println(2) + return e + } + + return nil +} diff --git a/Encryption/Encrypt.go b/Encryption/Encrypt.go new file mode 100644 index 0000000..b04eef0 --- /dev/null +++ b/Encryption/Encrypt.go @@ -0,0 +1,71 @@ +package Encryption + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "io" + "io/ioutil" + "os" +) + +func EncryptData(password string, data []byte) ([]byte, error) { + var ( + hashedKey []byte + ciphertext []byte + iv []byte + block cipher.Block + stream cipher.Stream + e error + ) + + hashedKey = CreateHash(password) + ciphertext = make([]byte, aes.BlockSize+len(hashedKey)+len(data)) + iv = ciphertext[:aes.BlockSize] + if _, e = io.ReadFull(rand.Reader, iv); e != nil { + return []byte{}, e + } + block, e = CreateKey(hashedKey) + if e != nil { + return []byte{}, e + } + stream = cipher.NewCFBEncrypter(block, iv) + stream.XORKeyStream(ciphertext[aes.BlockSize:], []byte(hashedKey)) + stream.XORKeyStream(ciphertext[aes.BlockSize+len([]byte(hashedKey)):], data) + return ciphertext, nil +} + +func EncryptFile(password string, FilePath string) error { + var ( + plaintext []byte + ciphertext []byte + encryptedFile *os.File + e error + ) + plaintext, e = ioutil.ReadFile(FilePath) + if e != nil { + return e + } + ciphertext, e = EncryptData(password, plaintext) + if e != nil { + return e + } + // open output file + encryptedFile, e = os.Create(FilePath + ".enc") + if e != nil { + return e + } + + defer func() { + encryptedFile.Close() + SecureDelete(FilePath) + }() + + _, e = io.Copy(encryptedFile, bytes.NewReader(ciphertext)) + if e != nil { + return e + } + + return nil +} diff --git a/Encryption/SecureDelete.go b/Encryption/SecureDelete.go new file mode 100644 index 0000000..cb17c0e --- /dev/null +++ b/Encryption/SecureDelete.go @@ -0,0 +1,34 @@ +package Encryption + +import ( + "os" +) + +func SecureDelete(FilePath string) error { + var ( + file *os.File + fileInfo os.FileInfo + size int64 + zeroBytes []byte + e error + ) + file, _ = os.OpenFile(FilePath, os.O_RDWR, 0666) + defer file.Close() + // Find out how large is the target file + fileInfo, e = file.Stat() + if e != nil { + return e + } + size = fileInfo.Size() + // Create byte array filled with zero's + zeroBytes = make([]byte, size) + _, e = file.Write([]byte(zeroBytes)) + if e != nil { + return e + } + e = os.Remove(FilePath) + if e != nil { + return e + } + return nil +} diff --git a/fenc b/fenc new file mode 100755 index 0000000..4a391ad Binary files /dev/null and b/fenc differ diff --git a/main.go b/main.go new file mode 100644 index 0000000..1545c66 --- /dev/null +++ b/main.go @@ -0,0 +1,107 @@ +package main + +import ( + "flag" + "fmt" + "os" + "syscall" + + "golang.org/x/crypto/ssh/terminal" + + "gitlab.com/tovijaeschke/FileEncryption/Encryption" +) + +func getPassword() (string, error) { + var ( + bytePassword []byte + e error + ) + fmt.Print("Enter Password: ") + bytePassword, e = terminal.ReadPassword(int(syscall.Stdin)) + if e != nil { + return "", e + } + return string(bytePassword), nil +} + +func main() { + var ( + files []string + decrypt *bool + read *bool + edit *bool + + plaintext string + password string + e error + ) + + flag.Usage = func() { + fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [OPTIONS]... [FILES]...\n", os.Args[0]) + fmt.Println("\t-d: Decrypt files") + fmt.Println("\t-r: Decrypt and read files to stdout") + fmt.Println("\t-e: Edit encrypted file") + } + + decrypt = flag.Bool("d", false, "Decrypt the file(s)") + read = flag.Bool("r", false, "Read file to stdout") + edit = flag.Bool("e", false, "Edit encrypted file") + flag.Parse() + files = flag.Args() + + if len(files) == 0 { + flag.Usage() + fmt.Println("Files cannot be null") + os.Exit(1) + } + + password, e = getPassword() + if e != nil { + fmt.Println(e.Error()) + os.Exit(1) + } + + if *decrypt { + for _, file := range files { + e = Encryption.DecryptFile(password, file) + if e != nil { + fmt.Println(e.Error()) + os.Exit(1) + } + } + os.Exit(0) + } + + if *read { + for _, file := range files { + plaintext, e = Encryption.DecryptAndReadFile(password, file) + if e != nil { + fmt.Println(e.Error()) + os.Exit(1) + } + fmt.Println(file) + fmt.Println(plaintext) + } + os.Exit(0) + } + + if *edit { + for _, file := range files { + e = Encryption.EditEncryptedFile(password, file) + if e != nil { + fmt.Println(e.Error()) + os.Exit(1) + } + } + os.Exit(0) + } + + + for _, file := range files { + e = Encryption.EncryptFile(password, file) + if e != nil { + fmt.Println(e.Error()) + os.Exit(1) + } + } +}