Compare commits
No commits in common. "c942a33e2ecf599b87cf2660b12128fb9ce0847c" and "9182bca042d9d557512e3a969c7e5b53256d8cd8" have entirely different histories.
c942a33e2e
...
9182bca042
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
/.idea/
|
|
||||||
/simple-privacy-tool
|
|
||||||
/spt
|
|
||||||
@ -1 +0,0 @@
|
|||||||
golang 1.20.5
|
|
||||||
31
README.md
31
README.md
@ -1,33 +1,2 @@
|
|||||||
# simple-privacy-tool
|
# simple-privacy-tool
|
||||||
|
|
||||||
Simple Privacy Tool is a simple tool to encrypt and decrypt files. It uses the symmetric algorithm XChaCha-Poly1305 and
|
|
||||||
Argon2id for the key derivation function.
|
|
||||||
|
|
||||||
Since this tool uses a symmetric algorithm, the level of privacy hinges solely on the password's strength. So, make sure
|
|
||||||
to choose your password carefully.
|
|
||||||
|
|
||||||
### Build
|
|
||||||
|
|
||||||
```shell
|
|
||||||
go build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
#### Encrypt
|
|
||||||
Encrypting `plainfile` to `cryptedfile`
|
|
||||||
```shell
|
|
||||||
simple-privacy-tool encrypt plainfile cryptedfile
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Decrypt
|
|
||||||
Decrypting `cryptedfile` back to `plainfile`
|
|
||||||
```shell
|
|
||||||
simple-privacy-tool decrypt cryptedfile plainfile
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Using STDIN/STDOUT
|
|
||||||
`simple-privacy-tool` can operate on `STDOUT`. Just replace the file path with `-`
|
|
||||||
```shell
|
|
||||||
simple-privacy-tool encrypt srcFile - | another-command
|
|
||||||
```
|
|
||||||
|
|||||||
@ -1,113 +0,0 @@
|
|||||||
package spt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"gitea.suyono.dev/suyono/simple-privacy-tool/privacy"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
type decryptApp struct {
|
|
||||||
cApp
|
|
||||||
r *privacy.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
type stdoutWrapper struct {
|
|
||||||
file *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDecryptApp(a *cApp) *decryptApp {
|
|
||||||
return &decryptApp{
|
|
||||||
cApp: *a,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decryptApp) GetPassphrase() (err error) {
|
|
||||||
var (
|
|
||||||
passphrase string
|
|
||||||
)
|
|
||||||
|
|
||||||
if passphrase, err = d.term.ReadPassword("input passphrase: "); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
d.passphrase = passphrase
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decryptApp) ProcessFiles() (err error) {
|
|
||||||
d.r = privacy.NewPrivacyReader(d.srcFile)
|
|
||||||
if err = d.r.ReadMagic(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = d.r.GenerateKey(d.passphrase); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.dstPath == "-" || d.srcPath == "-" {
|
|
||||||
w := stdoutWrapper{
|
|
||||||
file: d.dstFile,
|
|
||||||
}
|
|
||||||
if _, err = io.Copy(w, d.r); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if _, err = io.Copy(d.dstFile, d.r); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = d.dstFile.Close(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = d.srcFile.Close(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sw stdoutWrapper) ReadFrom(reader io.Reader) (n int64, err error) {
|
|
||||||
var (
|
|
||||||
nr int
|
|
||||||
rErr error
|
|
||||||
)
|
|
||||||
buf := make([]byte, 32768)
|
|
||||||
|
|
||||||
for {
|
|
||||||
if nr, err = reader.Read(buf); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
n += int64(nr)
|
|
||||||
if _, err = sw.file.Write(buf[:nr]); err != nil {
|
|
||||||
return n, fmt.Errorf("readfrom internal write: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rErr = err
|
|
||||||
if nr > 0 {
|
|
||||||
n += int64(nr)
|
|
||||||
if nr > 32768 {
|
|
||||||
return n, fmt.Errorf("last piece length %d: %w", nr, err)
|
|
||||||
}
|
|
||||||
if _, err = sw.file.Write(buf[:nr]); err != nil {
|
|
||||||
return n, fmt.Errorf("readfrom internal write: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rErr == io.EOF || rErr == nil {
|
|
||||||
err = nil
|
|
||||||
} else {
|
|
||||||
err = rErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sw stdoutWrapper) Write(b []byte) (n int, err error) {
|
|
||||||
return sw.file.Write(b)
|
|
||||||
}
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
package spt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gitea.suyono.dev/suyono/simple-privacy-tool/privacy"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
type encryptApp struct {
|
|
||||||
cApp
|
|
||||||
wc *privacy.WriteCloser
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEncryptApp(a *cApp) *encryptApp {
|
|
||||||
return &encryptApp{
|
|
||||||
cApp: *a,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encryptApp) GetPassphrase() (err error) {
|
|
||||||
var (
|
|
||||||
passphrase string
|
|
||||||
verify string
|
|
||||||
)
|
|
||||||
|
|
||||||
if passphrase, err = e.term.ReadPassword("input passphrase: "); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if verify, err = e.term.ReadPassword("verify - input passphrase: "); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if passphrase != verify {
|
|
||||||
return ErrPassphraseMismatch
|
|
||||||
}
|
|
||||||
|
|
||||||
e.passphrase = passphrase
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encryptApp) ProcessFiles() (err error) {
|
|
||||||
e.wc = privacy.NewPrivacyWriteCloser(e.dstFile, privacy.DefaultCipherMethod) //TODO: need to handle when custom keygen accepted
|
|
||||||
if err = e.wc.NewSalt(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = e.wc.GenerateKey(e.passphrase); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = io.Copy(e.wc, e.srcFile); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = e.wc.Close(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = e.srcFile.Close(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
162
cmd/spt/spt.go
162
cmd/spt/spt.go
@ -1,162 +0,0 @@
|
|||||||
package spt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
tw "gitea.suyono.dev/suyono/terminal_wrapper"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
type cApp struct {
|
|
||||||
term *tw.Terminal
|
|
||||||
srcPath string
|
|
||||||
dstPath string
|
|
||||||
srcFile *os.File
|
|
||||||
dstFile *os.File
|
|
||||||
passphrase string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
//ErrFatalError = errors.New("fatal error occurred")
|
|
||||||
ErrPassphraseMismatch = errors.New("mismatch passphrase")
|
|
||||||
|
|
||||||
rootCmd = &cobra.Command{
|
|
||||||
Use: "spt",
|
|
||||||
Short: "a simple tool to encrypt and decrypt file",
|
|
||||||
}
|
|
||||||
|
|
||||||
encryptCmd = &cobra.Command{
|
|
||||||
Use: "encrypt",
|
|
||||||
Args: validatePositionalArgs,
|
|
||||||
RunE: encrypt,
|
|
||||||
}
|
|
||||||
|
|
||||||
decryptCmd = &cobra.Command{
|
|
||||||
Use: "decrypt",
|
|
||||||
RunE: decrypt,
|
|
||||||
Args: validatePositionalArgs,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func Execute() error {
|
|
||||||
return rootCmd.Execute()
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.AddCommand(encryptCmd, decryptCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func validatePositionalArgs(cmd *cobra.Command, args []string) error {
|
|
||||||
if len(args) != 0 && len(args) != 2 {
|
|
||||||
//TODO: improve the error message
|
|
||||||
return errors.New("invalid arguments")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func encrypt(cmd *cobra.Command, args []string) (err error) {
|
|
||||||
var (
|
|
||||||
terminal *tw.Terminal
|
|
||||||
app *cApp
|
|
||||||
eApp *encryptApp
|
|
||||||
)
|
|
||||||
|
|
||||||
if terminal, err = tw.MakeTerminal(os.Stderr); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
existingErr := err
|
|
||||||
if err = terminal.Restore(); err == nil && existingErr != nil {
|
|
||||||
err = existingErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
app = &cApp{
|
|
||||||
term: terminal,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = app.ProcessArgs(args); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
eApp = newEncryptApp(app)
|
|
||||||
|
|
||||||
if err = eApp.GetPassphrase(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: process additional flags
|
|
||||||
|
|
||||||
if err = eApp.ProcessFiles(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func decrypt(cmd *cobra.Command, args []string) (err error) {
|
|
||||||
var (
|
|
||||||
terminal *tw.Terminal
|
|
||||||
app *cApp
|
|
||||||
dApp *decryptApp
|
|
||||||
)
|
|
||||||
|
|
||||||
if terminal, err = tw.MakeTerminal(os.Stderr); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
existingErr := err
|
|
||||||
if err = terminal.Restore(); err == nil && existingErr != nil {
|
|
||||||
err = existingErr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
app = &cApp{
|
|
||||||
term: terminal,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = app.ProcessArgs(args); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
dApp = newDecryptApp(app)
|
|
||||||
|
|
||||||
if err = dApp.GetPassphrase(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: process additional flags
|
|
||||||
|
|
||||||
if err = dApp.ProcessFiles(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *cApp) ProcessArgs(args []string) (err error) {
|
|
||||||
if len(args) == 0 {
|
|
||||||
a.srcFile = os.Stdin
|
|
||||||
a.dstFile = os.Stdout
|
|
||||||
} else {
|
|
||||||
a.srcPath = args[0]
|
|
||||||
a.dstPath = args[1]
|
|
||||||
if a.srcPath == "-" {
|
|
||||||
a.srcFile = os.Stdin
|
|
||||||
} else {
|
|
||||||
if a.srcFile, err = os.Open(a.srcPath); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.dstPath == "-" {
|
|
||||||
a.dstFile = os.Stdout
|
|
||||||
} else {
|
|
||||||
if a.dstFile, err = os.OpenFile(a.dstPath, os.O_CREATE|os.O_WRONLY, 0640); err != nil { //TODO: allow user to define the destination file permission
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
16
go.mod
16
go.mod
@ -1,16 +0,0 @@
|
|||||||
module gitea.suyono.dev/suyono/simple-privacy-tool
|
|
||||||
|
|
||||||
go 1.20
|
|
||||||
|
|
||||||
require (
|
|
||||||
gitea.suyono.dev/suyono/terminal_wrapper v0.0.0-20230722101024-a3e50949f40f
|
|
||||||
github.com/spf13/cobra v1.7.0
|
|
||||||
golang.org/x/crypto v0.11.0
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
|
||||||
golang.org/x/sys v0.10.0 // indirect
|
|
||||||
golang.org/x/term v0.10.0 // indirect
|
|
||||||
)
|
|
||||||
18
go.sum
18
go.sum
@ -1,18 +0,0 @@
|
|||||||
gitea.suyono.dev/suyono/terminal_wrapper v0.0.0-20230722101024-a3e50949f40f h1:yPac52dWHSutQH99FUhqz9rt2FHKaHr9srdJuDI9pkg=
|
|
||||||
gitea.suyono.dev/suyono/terminal_wrapper v0.0.0-20230722101024-a3e50949f40f/go.mod h1:I8qBJ8oaj+RwbLggXWNRrvRCDD+/bnRoQne0V4xsBNU=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
|
||||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
|
||||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
||||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
|
||||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
|
||||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
|
|
||||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
14
main.go
14
main.go
@ -1,14 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"gitea.suyono.dev/suyono/simple-privacy-tool/cmd/spt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
err := spt.Execute()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("error: %v\n", err)
|
|
||||||
//panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
package privacy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"golang.org/x/crypto/argon2"
|
|
||||||
)
|
|
||||||
|
|
||||||
type argon2Params struct {
|
|
||||||
Time uint32
|
|
||||||
Memory uint32
|
|
||||||
Threads uint8
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
var ErrInvalidParameter = errors.New("invalid parameter")
|
|
||||||
|
|
||||||
const argon2KeyGenName = "argon2"
|
|
||||||
|
|
||||||
func (a argon2Params) GenerateKey(password, salt []byte) []byte {
|
|
||||||
return argon2.IDKey(password, salt, a.Time, a.Memory, a.Threads, 32)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewArgon2() KeyGen {
|
|
||||||
return argon2Params{
|
|
||||||
Time: 1,
|
|
||||||
Memory: 64 * 1024,
|
|
||||||
Threads: 4,
|
|
||||||
Name: argon2KeyGenName,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewArgon2WithParams(time, memory uint32, threads uint8) (k KeyGen, err error) {
|
|
||||||
if time == 0 || memory == 0 || threads == 0 {
|
|
||||||
return nil, ErrInvalidParameter
|
|
||||||
}
|
|
||||||
|
|
||||||
return argon2Params{
|
|
||||||
Time: time,
|
|
||||||
Memory: memory,
|
|
||||||
Threads: threads,
|
|
||||||
Name: argon2KeyGenName,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a argon2Params) MarshalJSON() ([]byte, error) {
|
|
||||||
m := map[string]any{
|
|
||||||
"name": a.Name,
|
|
||||||
"memory": a.Memory,
|
|
||||||
"threads": a.Threads,
|
|
||||||
"time": a.Time,
|
|
||||||
}
|
|
||||||
return json.Marshal(&m)
|
|
||||||
}
|
|
||||||
@ -1,169 +0,0 @@
|
|||||||
package privacy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewArgon2(t *testing.T) {
|
|
||||||
k := NewArgon2()
|
|
||||||
if _, ok := k.(argon2Params); !ok {
|
|
||||||
t.Fatal("unexpected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewArgon2WithParams(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
time uint32
|
|
||||||
memory uint32
|
|
||||||
threads uint8
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "positive",
|
|
||||||
args: args{
|
|
||||||
time: 1,
|
|
||||||
memory: 4 * 1024,
|
|
||||||
threads: 4,
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "negative: zero time",
|
|
||||||
args: args{
|
|
||||||
time: 0,
|
|
||||||
memory: 1 * 1024,
|
|
||||||
threads: 4,
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "negative: zero memory",
|
|
||||||
args: args{
|
|
||||||
time: 1,
|
|
||||||
memory: 0,
|
|
||||||
threads: 4,
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "negative: zero threads",
|
|
||||||
args: args{
|
|
||||||
time: 1,
|
|
||||||
memory: 1 * 1024,
|
|
||||||
threads: 0,
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
_, err := NewArgon2WithParams(tt.args.time, tt.args.memory, tt.args.threads)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("NewArgon2WithParams() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArgon2Params_MarshalJSON(t *testing.T) {
|
|
||||||
var (
|
|
||||||
b []byte
|
|
||||||
err error
|
|
||||||
str string
|
|
||||||
ok bool
|
|
||||||
a any
|
|
||||||
)
|
|
||||||
|
|
||||||
if b, err = NewArgon2().MarshalJSON(); err != nil {
|
|
||||||
t.Fatal("unexpected", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m := make(map[string]any)
|
|
||||||
if err = json.Unmarshal(b, &m); err != nil {
|
|
||||||
t.Fatal("unexpected", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if a, ok = m["name"]; !ok {
|
|
||||||
t.Fatal("unexpected: no field name")
|
|
||||||
}
|
|
||||||
|
|
||||||
if str, ok = a.(string); !ok {
|
|
||||||
t.Fatal("unexpected: name field is not a string")
|
|
||||||
}
|
|
||||||
|
|
||||||
if str != argon2KeyGenName {
|
|
||||||
t.Fatal("unexpected: value of the name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_argon2Params_GenerateKey(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
Time uint32
|
|
||||||
Memory uint32
|
|
||||||
Threads uint8
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
password []byte
|
|
||||||
salt []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
prepKG KeyGen
|
|
||||||
prepErr error
|
|
||||||
)
|
|
||||||
|
|
||||||
passphrase := "some passphrase"
|
|
||||||
salt := make([]byte, 16)
|
|
||||||
if _, prepErr = rand.Read(salt); prepErr != nil {
|
|
||||||
t.Fatal("test preparation failure:", prepErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if prepKG, prepErr = NewArgon2WithParams(1, 1*1024, 2); prepErr != nil {
|
|
||||||
t.Fatal("test preparation failure:", prepErr)
|
|
||||||
}
|
|
||||||
prepBytes := prepKG.GenerateKey([]byte(passphrase), salt)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want []byte
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "positive",
|
|
||||||
fields: fields{
|
|
||||||
Time: 1,
|
|
||||||
Memory: 1 * 1024,
|
|
||||||
Threads: 2,
|
|
||||||
Name: argon2KeyGenName,
|
|
||||||
},
|
|
||||||
args: args{
|
|
||||||
password: []byte(passphrase),
|
|
||||||
salt: salt,
|
|
||||||
},
|
|
||||||
want: prepBytes,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
a := argon2Params{
|
|
||||||
Time: tt.fields.Time,
|
|
||||||
Memory: tt.fields.Memory,
|
|
||||||
Threads: tt.fields.Threads,
|
|
||||||
Name: tt.fields.Name,
|
|
||||||
}
|
|
||||||
if got := a.GenerateKey(tt.args.password, tt.args.salt); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("GenerateKey() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,368 +0,0 @@
|
|||||||
package privacy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CipherMethodType byte
|
|
||||||
|
|
||||||
type KeyGen interface {
|
|
||||||
json.Marshaler
|
|
||||||
GenerateKey(password, salt []byte) []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
segmentSizeBytesLen int = 4
|
|
||||||
|
|
||||||
Uninitialised CipherMethodType = 0
|
|
||||||
XChaCha20Simple CipherMethodType = 1
|
|
||||||
AES256GCMSimple CipherMethodType = 2
|
|
||||||
|
|
||||||
DefaultCipherMethod = XChaCha20Simple
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidSaltLen = errors.New("invalid salt length")
|
|
||||||
ErrUninitialisedSalt = errors.New("uninitialised salt")
|
|
||||||
ErrUninitialisedMethod = errors.New("cipher method type uninitialised")
|
|
||||||
ErrInvalidCipherMethod = errors.New("invalid cipher method type")
|
|
||||||
//ErrCannotReadMagicBytes = errors.New("cannot read magic bytes") //no usage for now
|
|
||||||
ErrInvalidReadFlow = errors.New("func ReadMagic should be called before calling Read")
|
|
||||||
ErrInvalidKeyState = errors.New("func GenerateKey should be called first")
|
|
||||||
ErrInvalidSegmentLength = errors.New("segment length is too long")
|
|
||||||
segmentLenBytes = make([]byte, segmentSizeBytesLen)
|
|
||||||
)
|
|
||||||
|
|
||||||
type Reader struct {
|
|
||||||
*Privacy
|
|
||||||
reader io.Reader
|
|
||||||
buf []byte
|
|
||||||
bufSlice []byte
|
|
||||||
isEOF bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type WriteCloser struct {
|
|
||||||
*Privacy
|
|
||||||
writeCloser io.WriteCloser
|
|
||||||
buf []byte
|
|
||||||
bufSlice []byte
|
|
||||||
magicWritten bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type Privacy struct {
|
|
||||||
salt []byte
|
|
||||||
segmentSize uint32
|
|
||||||
cmType CipherMethodType
|
|
||||||
aead cipher.AEAD
|
|
||||||
keygen KeyGen
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPrivacy(k KeyGen) *Privacy {
|
|
||||||
return &Privacy{
|
|
||||||
segmentSize: 64 * 1024 * 1024,
|
|
||||||
cmType: Uninitialised,
|
|
||||||
keygen: k,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrivacyReader(reader io.Reader) *Reader {
|
|
||||||
return NewPrivacyReaderWithKeyGen(reader, NewArgon2())
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrivacyReaderWithKeyGen(reader io.Reader, keygen KeyGen) *Reader {
|
|
||||||
return &Reader{
|
|
||||||
Privacy: newPrivacy(keygen),
|
|
||||||
reader: reader,
|
|
||||||
isEOF: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrivacyWriterCloserDefault(wc io.WriteCloser) *WriteCloser {
|
|
||||||
return NewPrivacyWriteCloser(wc, DefaultCipherMethod)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrivacyWriteCloser(wc io.WriteCloser, cmType CipherMethodType) *WriteCloser {
|
|
||||||
return NewPrivacyWriteCloserWithKeyGen(wc, cmType, NewArgon2())
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrivacyWriteCloserWithKeyGen(wc io.WriteCloser, cmType CipherMethodType, keygen KeyGen) *WriteCloser {
|
|
||||||
privacy := newPrivacy(keygen)
|
|
||||||
privacy.cmType = cmType
|
|
||||||
return &WriteCloser{
|
|
||||||
Privacy: privacy,
|
|
||||||
writeCloser: wc,
|
|
||||||
magicWritten: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Privacy) SetSalt(salt []byte) error {
|
|
||||||
if len(salt) != 16 {
|
|
||||||
return ErrInvalidSaltLen
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(p.salt) != 16 {
|
|
||||||
p.salt = make([]byte, 16)
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(p.salt, salt)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Privacy) GetSegmentSize() uint32 {
|
|
||||||
return p.segmentSize
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Privacy) SetSegmentSize(size uint32) {
|
|
||||||
p.segmentSize = size
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Privacy) NewSalt() error {
|
|
||||||
if len(p.salt) != 16 {
|
|
||||||
p.salt = make([]byte, 16)
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.cmType == Uninitialised {
|
|
||||||
return ErrUninitialisedMethod
|
|
||||||
}
|
|
||||||
|
|
||||||
p.salt[0] = byte(p.cmType)
|
|
||||||
_, err := rand.Read(p.salt[1:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Privacy) GenerateKey(passphrase string) error {
|
|
||||||
var (
|
|
||||||
key []byte
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
if p.cmType == Uninitialised {
|
|
||||||
return ErrUninitialisedMethod
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(p.salt) != 16 {
|
|
||||||
return ErrUninitialisedSalt
|
|
||||||
}
|
|
||||||
|
|
||||||
key = p.keygen.GenerateKey([]byte(passphrase), p.salt)
|
|
||||||
switch p.cmType {
|
|
||||||
case XChaCha20Simple:
|
|
||||||
p.aead, err = chacha20poly1305.NewX(key)
|
|
||||||
default:
|
|
||||||
return ErrInvalidCipherMethod
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WriteCloser) Write(b []byte) (n int, err error) {
|
|
||||||
var (
|
|
||||||
copied int
|
|
||||||
nonceSize int
|
|
||||||
lastMarker int
|
|
||||||
plaintext []byte
|
|
||||||
)
|
|
||||||
|
|
||||||
if wc.aead == nil {
|
|
||||||
return 0, ErrInvalidKeyState
|
|
||||||
}
|
|
||||||
|
|
||||||
if cap(wc.buf) != int(wc.segmentSize)+wc.aead.NonceSize()+wc.aead.Overhead() {
|
|
||||||
wc.buf = make([]byte, int(wc.segmentSize)+wc.aead.NonceSize()+wc.aead.Overhead())
|
|
||||||
wc.bufSlice = wc.buf[wc.aead.NonceSize():wc.aead.NonceSize()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if !wc.magicWritten {
|
|
||||||
n, err = wc.writeCloser.Write(wc.salt)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
wc.magicWritten = true
|
|
||||||
}
|
|
||||||
|
|
||||||
nonceSize = wc.aead.NonceSize()
|
|
||||||
copied = 0
|
|
||||||
for copied < len(b) {
|
|
||||||
if len(wc.bufSlice) == int(wc.segmentSize) {
|
|
||||||
n, err = wc.writeSegment()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lastMarker = len(wc.bufSlice)
|
|
||||||
plaintext = wc.buf[nonceSize : nonceSize+len(wc.bufSlice)]
|
|
||||||
if len(b[copied:]) <= int(wc.segmentSize)-len(wc.bufSlice) {
|
|
||||||
plaintext = plaintext[:len(plaintext)+len(b[copied:])]
|
|
||||||
copied += copy(plaintext[lastMarker:], b[copied:])
|
|
||||||
} else {
|
|
||||||
plaintext = plaintext[:int(wc.segmentSize)]
|
|
||||||
copied += copy(plaintext[lastMarker:], b[copied:])
|
|
||||||
}
|
|
||||||
wc.bufSlice = plaintext
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return copied, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WriteCloser) writeSegment() (n int, err error) {
|
|
||||||
var (
|
|
||||||
nonce []byte
|
|
||||||
ciphertext []byte
|
|
||||||
plaintext []byte
|
|
||||||
written int
|
|
||||||
)
|
|
||||||
|
|
||||||
written = len(wc.bufSlice)
|
|
||||||
binary.LittleEndian.PutUint32(segmentLenBytes, uint32(written))
|
|
||||||
n, err = wc.writeCloser.Write(segmentLenBytes)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
nonce = wc.buf[:wc.aead.NonceSize()]
|
|
||||||
_, err = rand.Read(nonce)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
plaintext = wc.buf[wc.aead.NonceSize() : wc.aead.NonceSize()+written]
|
|
||||||
ciphertext = plaintext[:0]
|
|
||||||
|
|
||||||
wc.aead.Seal(ciphertext, nonce, plaintext, segmentLenBytes)
|
|
||||||
n, err = wc.writeCloser.Write(wc.buf[:written+wc.aead.NonceSize()+wc.aead.Overhead()])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
wc.bufSlice = wc.buf[wc.aead.NonceSize():wc.aead.NonceSize()]
|
|
||||||
|
|
||||||
return written, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WriteCloser) Close() (err error) {
|
|
||||||
if len(wc.bufSlice) > 0 {
|
|
||||||
_, err = wc.writeSegment()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return wc.writeCloser.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Reader) ReadMagic() (err error) {
|
|
||||||
if r.cmType == Uninitialised {
|
|
||||||
magic := make([]byte, 16)
|
|
||||||
_, err = r.reader.Read(magic[:1])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch CipherMethodType(magic[0]) {
|
|
||||||
case XChaCha20Simple:
|
|
||||||
r.cmType = XChaCha20Simple
|
|
||||||
_, err = r.reader.Read(magic[1:])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = r.SetSalt(magic)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return ErrInvalidCipherMethod
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Reader) Read(b []byte) (n int, err error) {
|
|
||||||
var (
|
|
||||||
segmentLen uint32
|
|
||||||
nonce []byte
|
|
||||||
ciphertext []byte
|
|
||||||
plaintext []byte
|
|
||||||
copied int
|
|
||||||
)
|
|
||||||
|
|
||||||
if r.cmType == Uninitialised {
|
|
||||||
return 0, ErrInvalidReadFlow
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.aead == nil {
|
|
||||||
return 0, ErrInvalidKeyState
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.isEOF {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
if cap(r.buf) != int(r.segmentSize)+r.aead.Overhead()+r.aead.NonceSize() {
|
|
||||||
r.buf = make([]byte, int(r.segmentSize)+r.aead.Overhead()+r.aead.NonceSize())
|
|
||||||
}
|
|
||||||
|
|
||||||
copied = 0
|
|
||||||
for copied < len(b) {
|
|
||||||
if len(r.bufSlice) == 0 {
|
|
||||||
n, err = r.reader.Read(segmentLenBytes)
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
if copied > 0 {
|
|
||||||
r.isEOF = true
|
|
||||||
return copied, nil
|
|
||||||
} else {
|
|
||||||
r.isEOF = true
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
segmentLen = binary.LittleEndian.Uint32(segmentLenBytes)
|
|
||||||
if segmentLen > r.segmentSize {
|
|
||||||
return 0, ErrInvalidSegmentLength
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err = r.reader.Read(r.buf[:int(segmentLen)+r.aead.Overhead()+r.aead.NonceSize()])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
nonce = r.buf[:r.aead.NonceSize()]
|
|
||||||
ciphertext = r.buf[r.aead.NonceSize() : r.aead.NonceSize()+int(segmentLen)+r.aead.Overhead()]
|
|
||||||
plaintext = ciphertext[:0]
|
|
||||||
|
|
||||||
if _, err = r.aead.Open(plaintext, nonce, ciphertext, segmentLenBytes); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
plaintext = plaintext[:int(segmentLen)]
|
|
||||||
r.bufSlice = plaintext
|
|
||||||
} else {
|
|
||||||
if len(b[copied:]) <= len(r.bufSlice) {
|
|
||||||
cp := copy(b[copied:], r.bufSlice)
|
|
||||||
r.bufSlice = r.bufSlice[cp:]
|
|
||||||
copied += cp
|
|
||||||
} else {
|
|
||||||
copied += copy(b[copied:], r.bufSlice)
|
|
||||||
r.bufSlice = r.buf[r.aead.NonceSize():r.aead.NonceSize()]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return copied, nil
|
|
||||||
}
|
|
||||||
@ -1,241 +0,0 @@
|
|||||||
package privacy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"golang.org/x/crypto/argon2"
|
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
|
||||||
"io"
|
|
||||||
mr "math/rand"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tBuffer struct {
|
|
||||||
buf []byte
|
|
||||||
rOff int
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTBuf(size int) *tBuffer {
|
|
||||||
return &tBuffer{
|
|
||||||
buf: make([]byte, 0, size),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tb *tBuffer) Read(b []byte) (n int, err error) {
|
|
||||||
if tb.rOff == len(tb.buf) {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(b)+tb.rOff <= len(tb.buf) {
|
|
||||||
copy(b, tb.buf[tb.rOff:])
|
|
||||||
tb.rOff += len(b)
|
|
||||||
return len(b), nil
|
|
||||||
} else {
|
|
||||||
copy(b[:len(tb.buf)-tb.rOff], tb.buf[tb.rOff:])
|
|
||||||
n = len(tb.buf) - tb.rOff
|
|
||||||
err = nil
|
|
||||||
tb.rOff = len(tb.buf)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tb *tBuffer) Write(b []byte) (n int, err error) {
|
|
||||||
if len(tb.buf)+len(b) > cap(tb.buf) {
|
|
||||||
return 0, errors.New("insufficient space")
|
|
||||||
}
|
|
||||||
|
|
||||||
wOff := len(tb.buf)
|
|
||||||
tb.buf = tb.buf[:wOff+len(b)]
|
|
||||||
copy(tb.buf[wOff:], b)
|
|
||||||
return len(b), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tb *tBuffer) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadWriteClose(t *testing.T) {
|
|
||||||
tb := newTBuf(70 * 1024)
|
|
||||||
keygen, err := NewArgon2WithParams(1, 4*1024, 2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("test preparation failure:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
passphrase := "some passphrase"
|
|
||||||
|
|
||||||
writer := NewPrivacyWriteCloserWithKeyGen(tb, DefaultCipherMethod, keygen)
|
|
||||||
|
|
||||||
t.Run("uninitialised salt", func(t *testing.T) {
|
|
||||||
err = writer.GenerateKey(passphrase)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("unexpected: it should error")
|
|
||||||
}
|
|
||||||
if err != ErrUninitialisedSalt {
|
|
||||||
t.Fatal("unexpected error result:", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
writer.SetSegmentSize(uint32(16 * 1024))
|
|
||||||
|
|
||||||
if err = writer.NewSalt(); err != nil {
|
|
||||||
t.Fatal("unexpected: NewSalt failed", err)
|
|
||||||
}
|
|
||||||
if err = writer.GenerateKey(passphrase); err != nil {
|
|
||||||
t.Fatal("unexpected: failed to generate key", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sha := sha256.New()
|
|
||||||
ur := mr.New(mr.NewSource(1))
|
|
||||||
bb := make([]byte, 1048)
|
|
||||||
|
|
||||||
var (
|
|
||||||
//bar int
|
|
||||||
n int
|
|
||||||
wl int
|
|
||||||
rl int
|
|
||||||
)
|
|
||||||
for i := 0; i < 63; i++ {
|
|
||||||
//bar = 1000 + ur.Intn(49)
|
|
||||||
//ur.Read(bb[:bar])
|
|
||||||
//sha.Write(bb[:bar])
|
|
||||||
//if n, err = writer.Write(bb[:bar]); err != nil {
|
|
||||||
// t.Fatal("unexpected: Write failed", err)
|
|
||||||
//}
|
|
||||||
ur.Read(bb)
|
|
||||||
sha.Write(bb)
|
|
||||||
if n, err = writer.Write(bb); err != nil {
|
|
||||||
t.Fatal("unexpected: Write failed", err)
|
|
||||||
}
|
|
||||||
wl += n
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = writer.Close(); err != nil {
|
|
||||||
t.Fatal("unexpected: Close failed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
writeHash := sha.Sum(nil)
|
|
||||||
t.Log("write hash:", hex.EncodeToString(writeHash))
|
|
||||||
|
|
||||||
reader := NewPrivacyReaderWithKeyGen(tb, keygen)
|
|
||||||
reader.SetSegmentSize(uint32(16 * 1024))
|
|
||||||
if err = reader.ReadMagic(); err != nil {
|
|
||||||
t.Fatal("unexpected: ReadMagic failed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = reader.GenerateKey(passphrase); err != nil {
|
|
||||||
t.Fatal("unexpected: GenerateKey failed", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sha.Reset()
|
|
||||||
err = nil
|
|
||||||
n = 0
|
|
||||||
for err == nil {
|
|
||||||
if n, err = reader.Read(bb); err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
t.Fatal("unexpected: Read failed", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rl += n
|
|
||||||
sha.Write(bb)
|
|
||||||
}
|
|
||||||
|
|
||||||
readHash := sha.Sum(nil)
|
|
||||||
t.Log("read hash:", hex.EncodeToString(readHash))
|
|
||||||
|
|
||||||
t.Log("wl", wl)
|
|
||||||
t.Log("rl", rl)
|
|
||||||
for i := range writeHash {
|
|
||||||
if readHash[i] != writeHash[i] {
|
|
||||||
t.Fatal("unexpected: mismatch hash")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTrial(t *testing.T) {
|
|
||||||
x := make([]byte, 20)
|
|
||||||
for i := range x {
|
|
||||||
x[i] = byte(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Log("len x:", len(x))
|
|
||||||
t.Log("cap x:", cap(x))
|
|
||||||
t.Log("x:", x)
|
|
||||||
|
|
||||||
y := x[5:13]
|
|
||||||
t.Log("len y:", len(y))
|
|
||||||
t.Log("cap y:", cap(y))
|
|
||||||
t.Log("y:", y)
|
|
||||||
|
|
||||||
z := y[10:15]
|
|
||||||
t.Log("len z:", len(z))
|
|
||||||
t.Log("cap z:", cap(z))
|
|
||||||
t.Log("z:", z)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeyGen(t *testing.T) {
|
|
||||||
salt := make([]byte, 16)
|
|
||||||
_, err := rand.Read(salt)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("unexpected error:", err)
|
|
||||||
}
|
|
||||||
passphrase := []byte("some passphrase")
|
|
||||||
|
|
||||||
key := argon2.IDKey(passphrase, salt, 1000, 64*1024, 8, 32)
|
|
||||||
|
|
||||||
_ = key
|
|
||||||
//keyCompare := argon2Params.IDKey(passphrase, salt, 100, 64*1024, 4, 32)
|
|
||||||
//
|
|
||||||
//for i := range key {
|
|
||||||
// if key[i] != keyCompare[i] {
|
|
||||||
// t.Fatal("unexpected result")
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExample(t *testing.T) {
|
|
||||||
passphrase := []byte("some passphrase")
|
|
||||||
salt := make([]byte, 16)
|
|
||||||
_, err := rand.Read(salt)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("error prepare salt", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
key := argon2.IDKey(passphrase, salt, 1, 4*1024, 2, 32)
|
|
||||||
|
|
||||||
var aead cipher.AEAD
|
|
||||||
if aead, err = chacha20poly1305.NewX(key); err != nil {
|
|
||||||
t.Fatal("chacha", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ur := mr.New(mr.NewSource(1))
|
|
||||||
bb := make([]byte, 1024+aead.NonceSize()+aead.Overhead())
|
|
||||||
|
|
||||||
if _, err = rand.Read(bb[:aead.NonceSize()]); err != nil {
|
|
||||||
t.Fatal("fill up nonce", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
additional := []byte("some additional data")
|
|
||||||
ur.Read(bb[aead.NonceSize() : aead.NonceSize()+1024])
|
|
||||||
before := sha256.Sum256(bb[aead.NonceSize() : aead.NonceSize()+1024])
|
|
||||||
|
|
||||||
aead.Seal(bb[aead.NonceSize():aead.NonceSize()], bb[:aead.NonceSize()], bb[aead.NonceSize():aead.NonceSize()+1024], additional)
|
|
||||||
|
|
||||||
if _, err = aead.Open(bb[aead.NonceSize():aead.NonceSize()],
|
|
||||||
bb[:aead.NonceSize()], bb[aead.NonceSize():aead.NonceSize()+1024+aead.Overhead()],
|
|
||||||
additional); err != nil {
|
|
||||||
t.Fatal("decrypt error", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
after := sha256.Sum256(bb[aead.NonceSize() : aead.NonceSize()+1024])
|
|
||||||
|
|
||||||
for i := range before {
|
|
||||||
if before[i] != after[i] {
|
|
||||||
t.Fatal("data corruption?")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user