fix(vault): handle salt correctly
This commit is contained in:
parent
5a918ed435
commit
1ae230bf8f
|
@ -1 +1 @@
|
|||
{"gitea_token":"iVL60jOT+Q5tRB+Z:AAAAAAAAAAA=:hHe1bYr+0mGiZkUgEfvsjCXwF+nAQWPq7OA0maawVPBQpEr9O0flCMP6aEn9LcFNYyhInrQdAu0=","github_token":"NQilBUAEKWlgRAjB:AAAAAAAAAAA=:rxA7R6ES2DQsQts9BzXxYRKnpSYoiDHNrnxy1aWuxlq6ljK9dW304S8NJWmUf1GOL7K5sC92nHE="}
|
||||
{"github_token":"mSbjFvJQXritbQEk:sx3nnZbjL54=:mKwwJPHMAXvb0vbRYdwhtCmyjvem7r/vBwYXxg86L2mN2XqgotvsC2kb1iYor4UOemwB9N+tXHE="}
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
# Files #
|
||||
#########
|
||||
|
||||
go.work
|
||||
|
||||
.buildr/**/*.hcl
|
||||
LICENSE
|
||||
README.md
|
||||
|
|
|
@ -9,13 +9,18 @@ import (
|
|||
|
||||
func Pbkdf2Deriver() KeyDeriver {
|
||||
return KeyDeriverFunc(func(passphrase string, existingSalt []byte) (key []byte, salt []byte) {
|
||||
salt = make([]byte, 8)
|
||||
if existingSalt == nil {
|
||||
existingSalt = make([]byte, 8)
|
||||
// http://www.ietf.org/rfc/rfc2898.txt
|
||||
// Salt.
|
||||
_, _ = rand.Read(salt)
|
||||
} else if len(existingSalt) >= 8 {
|
||||
copy(salt, existingSalt[:8])
|
||||
} else {
|
||||
copy(salt, existingSalt)
|
||||
_, _ = rand.Read(salt[len(existingSalt):])
|
||||
}
|
||||
|
||||
return pbkdf2.Key([]byte(passphrase), salt, 1000, 32, sha256.New), existingSalt
|
||||
return pbkdf2.Key([]byte(passphrase), salt, 1000, 32, sha256.New), salt
|
||||
})
|
||||
}
|
||||
|
|
134
internal/vault/pbkdf2_test.go
Normal file
134
internal/vault/pbkdf2_test.go
Normal file
|
@ -0,0 +1,134 @@
|
|||
package vault_test
|
||||
|
||||
import (
|
||||
"code.icb4dc0.de/buildr/buildr/internal/vault"
|
||||
"code.icb4dc0.de/prskr/go-pwgen"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPbkdf2Deriver(t *testing.T) {
|
||||
type args struct {
|
||||
passphrase string
|
||||
existingSalt []byte
|
||||
}
|
||||
type want struct {
|
||||
keyMatcher func(key []byte) error
|
||||
saltMatcher func(salt []byte) error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want want
|
||||
}{
|
||||
{
|
||||
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 {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
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
|
||||
}
|
Loading…
Reference in a new issue