mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-11-20 15:15:18 +00:00
361 lines
7.5 KiB
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")
|
|
}
|