hns/main.go

174 lines
3.6 KiB
Go

package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rand"
"fmt"
"io"
"io/ioutil"
"os"
"regexp"
"strings"
"syscall"
"github.com/h2non/filetype"
"golang.org/x/crypto/ssh/terminal"
)
var debug bool = true
var FileName string
var HDNFhead []byte = []byte("HDNF")
var hdnfType = filetype.NewType("hdn","application/hns")
func checkErr(err error) {
if err != nil {
if debug {
panic(err.Error())
} else {
os.Exit(1)
}
}
}
func keyMkr(passwd string) []byte {
key := md5.New()
key.Write([]byte(passwd))
return key.Sum(nil)
}
func encrypt(data []byte, key []byte) []byte {
block, err := aes.NewCipher(key)
checkErr(err)
gcm, err := cipher.NewGCM(block)
checkErr(err)
nonce := make([]byte, gcm.NonceSize())
_ , err= io.ReadFull(rand.Reader, nonce)
checkErr(err)
sealed := gcm.Seal(nonce, nonce, data, nil)
return sealed
}
func decrypt(sealed []byte, key []byte) []byte {
block, err := aes.NewCipher(key)
checkErr(err)
gcm, err := cipher.NewGCM(block)
nonceSize := gcm.NonceSize()
nonce, ctxt := sealed[:nonceSize], sealed[nonceSize:]
data, err := gcm.Open(nil, nonce, ctxt, nil)
checkErr(err)
return data
}
func file2data(filename string) []byte {
data, err := ioutil.ReadFile(filename)
checkErr(err)
return data
}
func data2file(filename string, data []byte) {
f, err := os.Create(filename)
checkErr(err)
defer f.Close()
f.Write(data)
}
func getMode() string {
var mode string
match, _ := regexp.Match(`.*\.hdn$`,[]byte(FileName))
if match {
return "s"
}
fmt.Print("mode: ")
fmt.Fscan(os.Stdin, &mode)
if mode == "q" {os.Exit(1)}
if mode != "h" && mode != "s" {
fmt.Println("not a mode")
fmt.Println("type 'h' for hide, 's' for show or 'q' to quit")
mode = getMode()
}
return mode
}
func readFn() string {
var filename string
arg := os.Args[1:]
if len(arg) != 1 {
fmt.Println("hns only takes file name as arg")
os.Exit(1)
}
fn := arg[0]
if _, err := os.Stat(fn); err == nil {
filename = fn
} else {
fmt.Println("No such file")
os.Exit(1)
}
return filename
}
func readPass() string {
pw, err := terminal.ReadPassword(int(syscall.Stdin))
checkErr(err)
return string(pw)
}
func checkPass() string {
var pw string
fmt.Print("password: ")
pw1 := readPass()
fmt.Print("\nagain: ")
pw2 := readPass()
fmt.Print("\n")
if pw1 == pw2 {
pw = pw1
} else {
fmt.Println("passwords don't match")
checkPass()
}
return pw
}
func headerAdd(data []byte) []byte {
return bytes.Join([][]byte{HDNFhead,data},nil)
}
func headerRemove(data []byte) []byte {
return bytes.TrimPrefix(data,HDNFhead)
}
func hdnfMatcher(buf []byte) bool {
return len(buf) > 1 && buf[0] == 0x48 && buf[1] == 0x44 && buf[2] == 0x4e && buf[3] == 0x46
}
func main() {
FileName = readFn()
filetype.AddMatcher(hdnfType, hdnfMatcher)
fn := FileName
fmt.Println(fn)
buf := file2data(FileName)
if filetype.IsType(buf,hdnfType) {
fmt.Println("this is an hidden file")
fmt.Print("password: ")
pw := readPass()
fmt.Print("\n")
key := keyMkr(pw)
secret := headerRemove(buf)
datafn := strings.TrimRight(fn,".hdn")
data := decrypt(secret, key)
data2file(datafn, data)
} else {
fmt.Println("not hidden")
pw := checkPass()
key := keyMkr(pw)
secret := headerAdd(encrypt(buf,key))
secretfn := fn + ".hdn"
clearfn := fn + ".origin"
err := os.Rename(fn,clearfn)
checkErr(err)
data2file(secretfn,secret)
fmt.Printf("think about shredding %s\n", clearfn)
}
}