// Copyright (C) 2019 Marius Schellenberger package crypto import ( "crypto/rand" "errors" "golang.org/x/crypto/argon2" "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/ssh/terminal" "io" "os" ) const ( SaltSize = 16 PrefixSize = SaltSize + chacha20poly1305.NonceSizeX ) func randomBytes(l int) (b []byte) { b = make([]byte, l) _, err := rand.Read(b) if err != nil { panic(err) } return } func kdf(password, salt []byte) []byte { return argon2.IDKey(password, salt, 1, 64*1024, 4, chacha20poly1305.KeySize) } func Password() ([]byte, error) { return terminal.ReadPassword(int(os.Stdin.Fd())) } func Encrypt(pass, src []byte, dst io.Writer) (err error) { salt := randomBytes(SaltSize) nonce := randomBytes(chacha20poly1305.NonceSizeX) aead, err := chacha20poly1305.NewX(kdf(pass, salt)) if err != nil { return } _, err = dst.Write(salt) if err != nil { return } _, err = dst.Write(nonce) if err != nil { return } _, err = dst.Write(aead.Seal(nil, nonce, src, nil)) return } func Decrypt(pass, src []byte, dst io.Writer) (err error) { if len(src) <= PrefixSize { return errors.New("input file corrupted") } nonce := src[SaltSize:PrefixSize] aead, err := chacha20poly1305.NewX(kdf(pass, src[:SaltSize])) if err != nil { return } dec, err := aead.Open(nil, nonce, src[PrefixSize:], nil) if err != nil { return } _, err = dst.Write(dec) return }