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