KeyStore interface to keep secrets
File KeyStore implementation to keep secrets in a json file
diff --git a/libs/keystore/filestore.go b/libs/keystore/filestore.go
new file mode 100644
index 0000000..5491563
--- /dev/null
+++ b/libs/keystore/filestore.go
@@ -0,0 +1,111 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package keystore
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
+ "sync"
+
+ "github.com/pkg/errors"
+)
+
+// FileStore is the key Store implementation storing the keys in a file
+type FileStore struct {
+ sync.RWMutex
+ filePath string
+ keys map[string][]byte
+}
+
+// NewFileStore creates a new FileStore
+func NewFileStore(filePath string) (Store, error) {
+ fs := &FileStore{
+ filePath: filePath,
+ keys: map[string][]byte{},
+ }
+
+ if err := fs.loadKeys(); err != nil {
+ return nil, err
+ }
+
+ return fs, nil
+}
+
+// Set stores multiple keys at once
+func (f *FileStore) Set(name string, key []byte) error {
+ f.Lock()
+ defer f.Unlock()
+
+ f.keys[name] = make([]byte, len(key))
+ copy(f.keys[name], key)
+
+ return f.storeKeys()
+}
+
+// Get retrieves multiple keys
+func (f *FileStore) Get(name string) ([]byte, error) {
+ f.RLock()
+ defer f.RUnlock()
+
+ key, ok := f.keys[name]
+ if !ok {
+ return nil, ErrKeyNotFound
+ }
+
+ return key, nil
+}
+
+// TODO: Lock the file
+
+func (f *FileStore) loadKeys() error {
+ rawKeys, err := ioutil.ReadFile(f.filePath)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ return errors.Wrap(err, "Load keys")
+ }
+
+ return json.Unmarshal(rawKeys, &(f).keys)
+}
+
+func (f *FileStore) storeKeys() error {
+ rawKeys, err := json.Marshal(f.keys)
+ if err != nil {
+ return err
+ }
+
+ // Get the file permissions
+ var perm os.FileMode
+ fi, err := os.Stat(f.filePath)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ return errors.Wrap(err, "Get key file permissions")
+ }
+ perm = 0600
+ } else {
+ perm = fi.Mode().Perm()
+ }
+
+ if err := ioutil.WriteFile(f.filePath, rawKeys, perm); err != nil {
+ return errors.Wrap(err, "Store keys")
+ }
+
+ return nil
+}
diff --git a/libs/keystore/filestore_test.go b/libs/keystore/filestore_test.go
new file mode 100644
index 0000000..7e946dd
--- /dev/null
+++ b/libs/keystore/filestore_test.go
@@ -0,0 +1,85 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package keystore
+
+import (
+ "bytes"
+ "crypto/rand"
+ "fmt"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+)
+
+func TestFileStore(t *testing.T) {
+ keys := map[string][]byte{"key1": []byte{1}, "key2": []byte{1, 2}, "key3": []byte{1, 2, 3}}
+
+ fn := tmpFileName()
+ defer func() {
+ if err := os.Remove(fn); err != nil {
+ t.Logf("Warning! Temp file could not be deleted (%v): %v", err, fn)
+ }
+ }()
+
+ fs, err := NewFileStore(fn)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Set keys
+ for k, v := range keys {
+ fs.Set(k, v)
+ }
+
+ // Get Keys
+ for name, v := range keys {
+ key, err := fs.Get(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(v, key) {
+ t.Errorf("Key not match: %v. Expected: %v, Found: %v", name, v, key)
+ }
+
+ }
+
+ fs1, err := NewFileStore(fn)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Get Keys
+ for name, v := range keys {
+ key, err := fs1.Get(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(v, key) {
+ t.Errorf("Key not match: %v. Expected: %v, Found: %v", name, v, key)
+ }
+
+ }
+}
+
+func tmpFileName() string {
+ rnd := make([]byte, 8)
+ rand.Read(rnd)
+ ts := time.Now().UnixNano()
+
+ return filepath.Join(os.TempDir(), fmt.Sprintf("keystore-%v-%x.tmp", ts, rnd))
+}
diff --git a/libs/keystore/keystore.go b/libs/keystore/keystore.go
new file mode 100644
index 0000000..8b321c7
--- /dev/null
+++ b/libs/keystore/keystore.go
@@ -0,0 +1,36 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+/*
+Package keystore - keep secrets
+*/
+package keystore
+
+import "github.com/pkg/errors"
+
+var (
+ // ErrKeyNotFound is returned when a key is not found in the store
+ ErrKeyNotFound = errors.New("Key not found")
+)
+
+// Store is the keystore interface
+type Store interface {
+ // Set stores multiple keys at once
+ Set(name string, key []byte) error
+ // Get retrieves multiple keys
+ Get(name string) ([]byte, error)
+}
diff --git a/libs/keystore/memorystore.go b/libs/keystore/memorystore.go
new file mode 100644
index 0000000..484a45f
--- /dev/null
+++ b/libs/keystore/memorystore.go
@@ -0,0 +1,59 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package keystore
+
+import (
+ "sync"
+)
+
+// MemoryStore is the in-memory implementation of key store
+type MemoryStore struct {
+ sync.RWMutex
+ keys map[string][]byte
+}
+
+// NewMemoryStore creates a new MemoryStore
+func NewMemoryStore() (Store, error) {
+ return &MemoryStore{
+ keys: map[string][]byte{},
+ }, nil
+}
+
+// Set stores multiple keys at once
+func (f *MemoryStore) Set(name string, key []byte) error {
+ f.Lock()
+ defer f.Unlock()
+
+ f.keys[name] = make([]byte, len(key))
+ copy(f.keys[name], key)
+
+ return nil
+}
+
+// Get retrieves multiple keys
+func (f *MemoryStore) Get(name string) ([]byte, error) {
+ f.RLock()
+ defer f.RUnlock()
+
+ key, ok := f.keys[name]
+ if !ok {
+ return nil, ErrKeyNotFound
+ }
+
+ return key, nil
+}
diff --git a/libs/keystore/memorystore_test.go b/libs/keystore/memorystore_test.go
new file mode 100644
index 0000000..5dd92a2
--- /dev/null
+++ b/libs/keystore/memorystore_test.go
@@ -0,0 +1,46 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package keystore
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestMemoryStore(t *testing.T) {
+ keys := map[string][]byte{"key1": []byte{1}, "key2": []byte{1, 2}, "key3": []byte{1, 2, 3}}
+
+ ms, err := NewMemoryStore()
+ if err != nil {
+ t.Fatal(err)
+ }
+ for k, v := range keys {
+ ms.Set(k, v)
+ }
+
+ for name, v := range keys {
+ key, err := ms.Get(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(v, key) {
+ t.Errorf("Key not match: %v. Expected: %v, Found: %v", name, v, key)
+ }
+
+ }
+}