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") }