140 lines
3 KiB
Go
140 lines
3 KiB
Go
package vault_test
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"code.icb4dc0.de/prskr/go-pwgen"
|
|
|
|
"code.icb4dc0.de/buildr/buildr/internal/vault"
|
|
)
|
|
|
|
func TestPbkdf2Deriver(t *testing.T) {
|
|
t.Parallel()
|
|
type args struct {
|
|
passphrase string
|
|
existingSalt []byte
|
|
}
|
|
type want struct {
|
|
keyMatcher func(key []byte) error
|
|
saltMatcher func(salt []byte) error
|
|
}
|
|
tests := []struct {
|
|
want want
|
|
name string
|
|
args args
|
|
}{
|
|
{
|
|
name: "Generated password, non-existing salt",
|
|
args: args{
|
|
passphrase: pwgen.MustGenerate(),
|
|
existingSalt: nil,
|
|
},
|
|
want: want{
|
|
keyMatcher: matchAll(matchNotEmpty, matchNotOnlyNullData),
|
|
saltMatcher: matchAll(matchNotEmpty, matchNotOnlyNullData),
|
|
},
|
|
},
|
|
{
|
|
name: "Generated password, existing salt of correct length",
|
|
args: args{
|
|
passphrase: pwgen.MustGenerate(),
|
|
existingSalt: randomOfLength(8),
|
|
},
|
|
want: want{
|
|
keyMatcher: matchAll(matchNotEmpty, matchNotOnlyNullData),
|
|
saltMatcher: matchAll(matchNotEmpty, matchNotOnlyNullData),
|
|
},
|
|
},
|
|
{
|
|
name: "Generated password, existing salt - too long",
|
|
args: args{
|
|
passphrase: pwgen.MustGenerate(),
|
|
existingSalt: randomOfLength(12),
|
|
},
|
|
want: want{
|
|
keyMatcher: matchAll(matchNotEmpty, matchNotOnlyNullData),
|
|
saltMatcher: matchAll(matchLength(8), matchNotOnlyNullData),
|
|
},
|
|
},
|
|
{
|
|
name: "Generated password, existing salt - too short",
|
|
args: args{
|
|
passphrase: pwgen.MustGenerate(),
|
|
existingSalt: randomOfLength(6),
|
|
},
|
|
want: want{
|
|
keyMatcher: matchAll(matchNotEmpty, matchNotOnlyNullData),
|
|
saltMatcher: matchAll(matchLength(8), matchNotOnlyNullData),
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
deriver := vault.Pbkdf2Deriver()
|
|
key, salt := deriver.DeriveKey(tt.args.passphrase, tt.args.existingSalt)
|
|
if tt.want.keyMatcher != nil {
|
|
if err := tt.want.keyMatcher(key); err != nil {
|
|
t.Errorf("Deriver.DeriveKey() error = %v", err)
|
|
}
|
|
}
|
|
|
|
if tt.want.saltMatcher != nil {
|
|
if err := tt.want.saltMatcher(salt); err != nil {
|
|
t.Errorf("Deriver.DeriveKey() error = %v", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func randomOfLength(length int) []byte {
|
|
s := make([]byte, length)
|
|
_, err := rand.Read(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return s
|
|
}
|
|
|
|
func matchAll(matchers ...func(key []byte) error) func(data []byte) error {
|
|
return func(data []byte) error {
|
|
for _, matcher := range matchers {
|
|
if err := matcher(data); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func matchLength(length int) func(data []byte) error {
|
|
return func(data []byte) error {
|
|
if len(data) != length {
|
|
return fmt.Errorf("length mismatch: expected %d, got %d", length, len(data))
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func matchNotOnlyNullData(data []byte) error {
|
|
var null byte
|
|
for i := range data {
|
|
if data[i] != null {
|
|
return nil
|
|
}
|
|
}
|
|
return errors.New("only null data")
|
|
}
|
|
|
|
func matchNotEmpty(data []byte) error {
|
|
if len(data) == 0 {
|
|
return errors.New("empty data")
|
|
}
|
|
return nil
|
|
}
|