Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement KeyStore options and DeleteEntry method #24

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 44 additions & 10 deletions keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"io"
"sort"
"strings"
"time"
)

Expand All @@ -25,6 +26,9 @@ const minPasswordLen = 6
// KeyStore is a mapping of alias to pointer to PrivateKeyEntry or TrustedCertificateEntry.
type KeyStore struct {
m map[string]interface{}

ordered bool
caseExact bool
}

// PrivateKeyEntry is an entry for private keys and associated certificates.
Expand All @@ -48,9 +52,23 @@ type Certificate struct {
Content []byte
}

type Option func(store *KeyStore)

// WithOrderedAliases sets ordered option to true. Orders aliases alphabetically.
func WithOrderedAliases() Option { return func(ks *KeyStore) { ks.ordered = true } }

// WithCaseExactAliases sets caseExact option to true. Preserves original case of aliases.
func WithCaseExactAliases() Option { return func(ks *KeyStore) { ks.caseExact = true } }

// New returns new initialized instance of the KeyStore.
func New() KeyStore {
return KeyStore{m: make(map[string]interface{})}
func New(options ...Option) KeyStore {
ks := KeyStore{m: make(map[string]interface{})}

for _, option := range options {
option(&ks)
}

return ks
}

// Store signs keystore using password and writes its representation into w
Expand Down Expand Up @@ -189,15 +207,15 @@ func (ks KeyStore) SetPrivateKeyEntry(alias string, entry PrivateKeyEntry, passw

entry.encryptedPrivateKey = epk

ks.m[alias] = entry
ks.m[ks.convertAlias(alias)] = entry

return nil
}

// GetPrivateKeyEntry returns PrivateKeyEntry from the keystore by the alias decrypted with the password.
// It is strongly recommended to fill password slice with zero after usage.
func (ks KeyStore) GetPrivateKeyEntry(alias string, password []byte) (PrivateKeyEntry, error) {
e, ok := ks.m[alias]
e, ok := ks.m[ks.convertAlias(alias)]
if !ok {
return PrivateKeyEntry{}, ErrEntryNotFound
}
Expand All @@ -220,7 +238,7 @@ func (ks KeyStore) GetPrivateKeyEntry(alias string, password []byte) (PrivateKey

// IsPrivateKeyEntry returns true if the keystore has PrivateKeyEntry by the alias.
func (ks KeyStore) IsPrivateKeyEntry(alias string) bool {
_, ok := ks.m[alias].(PrivateKeyEntry)
_, ok := ks.m[ks.convertAlias(alias)].(PrivateKeyEntry)

return ok
}
Expand All @@ -231,14 +249,14 @@ func (ks KeyStore) SetTrustedCertificateEntry(alias string, entry TrustedCertifi
return fmt.Errorf("validate trusted certificate entry: %w", err)
}

ks.m[alias] = entry
ks.m[ks.convertAlias(alias)] = entry

return nil
}

// GetTrustedCertificateEntry returns TrustedCertificateEntry from the keystore by the alias.
func (ks KeyStore) GetTrustedCertificateEntry(alias string) (TrustedCertificateEntry, error) {
e, ok := ks.m[alias]
e, ok := ks.m[ks.convertAlias(alias)]
if !ok {
return TrustedCertificateEntry{}, ErrEntryNotFound
}
Expand All @@ -253,23 +271,39 @@ func (ks KeyStore) GetTrustedCertificateEntry(alias string) (TrustedCertificateE

// IsTrustedCertificateEntry returns true if the keystore has TrustedCertificateEntry by the alias.
func (ks KeyStore) IsTrustedCertificateEntry(alias string) bool {
_, ok := ks.m[alias].(TrustedCertificateEntry)
_, ok := ks.m[ks.convertAlias(alias)].(TrustedCertificateEntry)

return ok
}

// Aliases returns slice of all aliases from the keystore sorted alphabetically.
// DeleteEntry deletes entry from the keystore.
func (ks KeyStore) DeleteEntry(alias string) {
delete(ks.m, ks.convertAlias(alias))
}

// Aliases returns slice of all aliases from the keystore.
// Aliases returns slice of all aliases sorted alphabetically if keystore created using WithOrderedAliases option.
func (ks KeyStore) Aliases() []string {
as := make([]string, 0, len(ks.m))
for a := range ks.m {
as = append(as, a)
}

sort.Strings(as)
if ks.ordered {
sort.Strings(as)
}

return as
}

func (ks KeyStore) convertAlias(alias string) string {
if ks.caseExact {
return alias
}

return strings.ToLower(alias)
}

func (e PrivateKeyEntry) validate() error {
if len(e.PrivateKey) == 0 {
return ErrEmptyPrivateKey
Expand Down
10 changes: 8 additions & 2 deletions keystore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"
"reflect"
"sort"
"testing"
"time"
)
Expand Down Expand Up @@ -153,8 +154,8 @@ func TestAliases(t *testing.T) {
}

const (
pkeAlias = "pkeAlias"
tceAlias = "tceAlias"
pkeAlias = "pke-alias"
tceAlias = "tce-alias"
)

if err := ks.SetPrivateKeyEntry(pkeAlias, pke, []byte("password")); err != nil {
Expand All @@ -166,8 +167,13 @@ func TestAliases(t *testing.T) {
}

expectedAliases := []string{pkeAlias, tceAlias}

sort.Strings(expectedAliases)

actualAliases := ks.Aliases()

sort.Strings(actualAliases)

if !reflect.DeepEqual(expectedAliases, actualAliases) {
t.Fatal("aliases must be equal")
}
Expand Down