ceremonyclient/node/keys/file.go

361 lines
7.5 KiB
Go

package keys
import (
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"os"
"sync"
"github.com/cloudflare/circl/sign/ed448"
"github.com/pkg/errors"
"go.uber.org/zap"
"gopkg.in/yaml.v2"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
"source.quilibrium.com/quilibrium/monorepo/node/config"
)
type FileKeyManager struct {
keyStoreConfig *config.KeyStoreFileConfig
logger *zap.Logger
key ByteString
store map[string]Key
storeMx sync.Mutex
}
var UnsupportedKeyTypeErr = errors.New("unsupported key type")
var KeyNotFoundErr = errors.New("key not found")
func NewFileKeyManager(
keyStoreConfig *config.KeyConfig,
logger *zap.Logger,
) *FileKeyManager {
if keyStoreConfig.KeyStoreFile == nil {
panic("key store config missing")
}
key, err := hex.DecodeString(keyStoreConfig.KeyStoreFile.EncryptionKey)
if err != nil {
panic(err)
}
store := make(map[string]Key)
flag := os.O_RDONLY
if keyStoreConfig.KeyStoreFile.CreateIfMissing {
flag |= os.O_CREATE
}
file, err := os.OpenFile(
keyStoreConfig.KeyStoreFile.Path,
flag,
os.FileMode(0600),
)
if err != nil {
panic(err)
}
defer file.Close()
d := yaml.NewDecoder(file)
if err := d.Decode(store); err != nil {
panic(err)
}
return &FileKeyManager{
keyStoreConfig: keyStoreConfig.KeyStoreFile,
logger: logger,
key: key,
store: store,
}
}
// CreateSigningKey implements KeyManager
func (f *FileKeyManager) CreateSigningKey(
id string,
keyType KeyType,
) (crypto.Signer, error) {
switch keyType {
case KeyTypeEd448:
pubkey, privkey, err := ed448.GenerateKey(rand.Reader)
if err != nil {
return nil, errors.Wrap(err, "could not generate key")
}
if err = f.save(
id,
Key{
Id: id,
Type: keyType,
PublicKey: ByteString(pubkey),
PrivateKey: ByteString(privkey),
},
); err != nil {
return nil, errors.Wrap(err, "could not save")
}
return privkey, nil
// case KeyTypePCAS:
// _, privkey, err := addressing.GenerateKey(rand.Reader)
// if err != nil {
// return nil, errors.Wrap(err, "could not generate key")
// }
// if err = f.save(id, privkey); err != nil {
// return nil, errors.Wrap(err, "could not save")
// }
// return privkey, nil
}
return nil, UnsupportedKeyTypeErr
}
// CreateAgreementKey implements KeyManager
func (f *FileKeyManager) CreateAgreementKey(
id string,
keyType KeyType,
) (curves.Scalar, error) {
switch keyType {
case KeyTypeX448:
privkey := curves.ED448().Scalar.Random(rand.Reader)
pubkey := curves.ED448().NewGeneratorPoint().Mul(privkey)
if err := f.save(
id,
Key{
Id: id,
Type: KeyTypeX448,
PublicKey: pubkey.ToAffineCompressed(),
PrivateKey: privkey.Bytes(),
},
); err != nil {
return nil, errors.Wrap(err, "could not save")
}
return privkey, nil
}
return nil, UnsupportedKeyTypeErr
}
// GetAgreementKey implements KeyManager
func (f *FileKeyManager) GetAgreementKey(id string) (curves.Scalar, error) {
key, err := f.read(id)
if err != nil {
return nil, err
}
switch key.Type {
case KeyTypeX448:
privkey, err := curves.ED448().NewScalar().SetBytes(key.PrivateKey)
return privkey, err
}
return nil, UnsupportedKeyTypeErr
}
// GetRawKey implements KeyManager
func (f *FileKeyManager) GetRawKey(id string) (*Key, error) {
key, err := f.read(id)
return &key, err
}
// GetSigningKey implements KeyManager
func (f *FileKeyManager) GetSigningKey(id string) (crypto.Signer, error) {
key, err := f.read(id)
if err != nil {
return nil, err
}
switch key.Type {
case KeyTypeEd448:
privkey := (ed448.PrivateKey)(key.PrivateKey)
return privkey, err
// case KeyTypePCAS:
// privkey := (addressing.PCAS)(key.PrivateKey)
// return privkey, err
}
return nil, UnsupportedKeyTypeErr
}
// PutRawKey implements KeyManager
func (f *FileKeyManager) PutRawKey(key *Key) error {
return f.save(key.Id, *key)
}
// DeleteKey implements KeyManager
func (f *FileKeyManager) DeleteKey(id string) error {
flag := os.O_RDWR
if f.keyStoreConfig.CreateIfMissing {
flag |= os.O_CREATE
}
file, err := os.OpenFile(f.keyStoreConfig.Path, flag, os.FileMode(0600))
if err != nil {
return errors.Wrap(err, "could not open store")
}
defer file.Close()
d := yaml.NewEncoder(file)
f.storeMx.Lock()
delete(f.store, id)
err = d.Encode(f.store)
f.storeMx.Unlock()
return errors.Wrap(err, "could not store")
}
// GetKey implements KeyManager
func (f *FileKeyManager) GetKey(id string) (key *Key, err error) {
storeKey, err := f.read(id)
if err != nil {
return nil, err
}
return &storeKey, nil
}
// ListKeys implements KeyManager
func (f *FileKeyManager) ListKeys() ([]*Key, error) {
keys := []*Key{}
f.storeMx.Lock()
for k := range f.store {
storeKey, err := f.read(k)
if err != nil {
return nil, err
}
keys = append(keys, &storeKey)
}
f.storeMx.Unlock()
return keys, nil
}
var _ KeyManager = (*FileKeyManager)(nil)
func (f *FileKeyManager) save(id string, key Key) error {
flag := os.O_RDWR
if f.keyStoreConfig.CreateIfMissing {
flag |= os.O_CREATE
}
file, err := os.OpenFile(f.keyStoreConfig.Path, flag, os.FileMode(0600))
if err != nil {
return errors.Wrap(err, "could not open store")
}
defer file.Close()
d := yaml.NewEncoder(file)
encKey := []byte{}
if encKey, err = f.encrypt(key.PrivateKey); err != nil {
return errors.Wrap(err, "could not encrypt")
}
f.storeMx.Lock()
f.store[id] = Key{
Id: key.Id,
Type: key.Type,
PublicKey: key.PublicKey,
PrivateKey: encKey,
}
err = d.Encode(f.store)
f.storeMx.Unlock()
return errors.Wrap(err, "could not store")
}
func (f *FileKeyManager) read(id string) (Key, error) {
flag := os.O_RDONLY
if f.keyStoreConfig.CreateIfMissing {
flag |= os.O_CREATE
}
file, err := os.OpenFile(f.keyStoreConfig.Path, flag, os.FileMode(0600))
if err != nil {
return Key{}, errors.Wrap(err, "could not open store")
}
defer file.Close()
d := yaml.NewDecoder(file)
f.storeMx.Lock()
if err = d.Decode(f.store); err != nil {
f.storeMx.Unlock()
return Key{}, errors.Wrap(err, "could not decode")
}
if _, ok := f.store[id]; !ok {
f.storeMx.Unlock()
return Key{}, KeyNotFoundErr
}
data, err := f.decrypt(f.store[id].PrivateKey)
if err != nil {
f.storeMx.Unlock()
return Key{}, errors.Wrap(err, "could not decrypt")
}
key := Key{
Id: f.store[id].Id,
Type: f.store[id].Type,
PublicKey: f.store[id].PublicKey,
PrivateKey: data,
}
f.storeMx.Unlock()
return key, nil
}
func (f *FileKeyManager) encrypt(data []byte) ([]byte, error) {
iv := [12]byte{}
rand.Read(iv[:])
aesCipher, err := aes.NewCipher(f.key)
if err != nil {
return nil, errors.Wrap(err, "could not construct cipher")
}
gcm, err := cipher.NewGCM(aesCipher)
if err != nil {
return nil, errors.Wrap(err, "could not construct block")
}
ciphertext := []byte{}
ciphertext = gcm.Seal(nil, iv[:], data, nil)
ciphertext = append(append([]byte{}, iv[:]...), ciphertext...)
return ciphertext, nil
}
func (f *FileKeyManager) decrypt(data []byte) ([]byte, error) {
iv := data[:12]
aesCipher, err := aes.NewCipher(f.key)
if err != nil {
return nil, errors.Wrap(err, "could not construct cipher")
}
gcm, err := cipher.NewGCM(aesCipher)
if err != nil {
return nil, errors.Wrap(err, "could not construct block")
}
ciphertext := data[12:]
plaintext, err := gcm.Open(nil, iv[:], ciphertext, nil)
return plaintext, errors.Wrap(err, "could not decrypt ciphertext")
}