Changed node ids to be derived from signing keys

gql_cataclysm
noah metz 2023-07-28 15:07:38 -06:00
parent b92cebbe74
commit 5fb1cb6d17
7 changed files with 98 additions and 20 deletions

@ -7,6 +7,7 @@ import (
"errors" "errors"
"runtime" "runtime"
"crypto/sha512" "crypto/sha512"
"crypto/elliptic"
"encoding/binary" "encoding/binary"
) )
@ -77,6 +78,8 @@ type Context struct {
Extensions map[uint64]ExtensionInfo Extensions map[uint64]ExtensionInfo
// Map between database type hashes and the registered info // Map between database type hashes and the registered info
Types map[uint64]*NodeInfo Types map[uint64]*NodeInfo
// Curve used for signature operations
ECDSA elliptic.Curve
// Routing map to all the nodes local to this context // Routing map to all the nodes local to this context
NodesLock sync.RWMutex NodesLock sync.RWMutex
Nodes map[NodeID]*Node Nodes map[NodeID]*Node
@ -194,6 +197,7 @@ func NewContext(db * badger.DB, log Logger) (*Context, error) {
Extensions: map[uint64]ExtensionInfo{}, Extensions: map[uint64]ExtensionInfo{},
Types: map[uint64]*NodeInfo{}, Types: map[uint64]*NodeInfo{},
Nodes: map[NodeID]*Node{}, Nodes: map[NodeID]*Node{},
ECDSA: elliptic.P256(),
} }
var err error var err error

@ -15,7 +15,7 @@ func TestGQLDB(t * testing.T) {
TestUserNodeType := NodeType("TEST_USER") TestUserNodeType := NodeType("TEST_USER")
err := ctx.RegisterNodeType(TestUserNodeType, []ExtType{}) err := ctx.RegisterNodeType(TestUserNodeType, []ExtType{})
fatalErr(t, err) fatalErr(t, err)
u1 := NewNode(ctx, RandID(), TestUserNodeType, 10, nil) u1 := NewNode(ctx, nil, TestUserNodeType, 10, nil)
ctx.Log.Logf("test", "U1_ID: %s", u1.ID) ctx.Log.Logf("test", "U1_ID: %s", u1.ID)
@ -24,7 +24,7 @@ func TestGQLDB(t * testing.T) {
gql_ext := NewGQLExt(":0", ecdh.P256(), key, nil, nil) gql_ext := NewGQLExt(":0", ecdh.P256(), key, nil, nil)
listener_ext := NewListenerExt(10) listener_ext := NewListenerExt(10)
gql := NewNode(ctx, RandID(), GQLNodeType, 10, nil, gql := NewNode(ctx, nil, GQLNodeType, 10, nil,
gql_ext, gql_ext,
listener_ext, listener_ext,
NewACLExt(), NewACLExt(),

@ -113,7 +113,7 @@ func NewSimpleListener(ctx *Context, buffer int) (*Node, *ListenerExt) {
policy := NewAllNodesPolicy(Actions{MakeAction("status")}) policy := NewAllNodesPolicy(Actions{MakeAction("status")})
listener_extension := NewListenerExt(buffer) listener_extension := NewListenerExt(buffer)
listener := NewNode(ctx, listener := NewNode(ctx,
RandID(), nil,
SimpleListenerNodeType, SimpleListenerNodeType,
10, 10,
nil, nil,

@ -25,13 +25,13 @@ func TestLink(t *testing.T) {
ctx := lockableTestContext(t, []string{}) ctx := lockableTestContext(t, []string{})
l1_listener := NewListenerExt(10) l1_listener := NewListenerExt(10)
l1 := NewNode(ctx, RandID(), TestLockableType, 10, nil, l1 := NewNode(ctx, nil, TestLockableType, 10, nil,
l1_listener, l1_listener,
NewACLExt(link_policy), NewACLExt(link_policy),
NewLockableExt(), NewLockableExt(),
) )
l2_listener := NewListenerExt(10) l2_listener := NewListenerExt(10)
l2 := NewNode(ctx, RandID(), TestLockableType, 10, nil, l2 := NewNode(ctx, nil, TestLockableType, 10, nil,
l2_listener, l2_listener,
NewACLExt(link_policy), NewACLExt(link_policy),
NewLockableExt(), NewLockableExt(),
@ -55,7 +55,7 @@ func TestLink10K(t *testing.T) {
ctx := lockableTestContext(t, []string{"test"}) ctx := lockableTestContext(t, []string{"test"})
NewLockable := func()(*Node) { NewLockable := func()(*Node) {
l := NewNode(ctx, RandID(), TestLockableType, 10, nil, l := NewNode(ctx, nil, TestLockableType, 10, nil,
NewACLExt(lock_policy, link_policy), NewACLExt(lock_policy, link_policy),
NewLockableExt(), NewLockableExt(),
) )
@ -64,7 +64,7 @@ func TestLink10K(t *testing.T) {
NewListener := func()(*Node, *ListenerExt) { NewListener := func()(*Node, *ListenerExt) {
listener := NewListenerExt(100000) listener := NewListenerExt(100000)
l := NewNode(ctx, RandID(), TestLockableType, 256, nil, l := NewNode(ctx, nil, TestLockableType, 256, nil,
listener, listener,
NewACLExt(lock_policy, link_policy), NewACLExt(lock_policy, link_policy),
NewLockableExt(), NewLockableExt(),
@ -94,7 +94,7 @@ func TestLock(t *testing.T) {
NewLockable := func()(*Node, *ListenerExt) { NewLockable := func()(*Node, *ListenerExt) {
listener := NewListenerExt(100) listener := NewListenerExt(100)
l := NewNode(ctx, RandID(), TestLockableType, 10, nil, l := NewNode(ctx, nil, TestLockableType, 10, nil,
listener, listener,
NewACLExt(lock_policy, link_policy), NewACLExt(lock_policy, link_policy),
NewLockableExt(), NewLockableExt(),

@ -10,13 +10,18 @@ import (
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"sync/atomic" "sync/atomic"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha512"
"crypto/rand"
"crypto/x509"
) )
const ( const (
// Magic first four bytes of serialized DB content, stored big endian // Magic first four bytes of serialized DB content, stored big endian
NODE_DB_MAGIC = 0x2491df14 NODE_DB_MAGIC = 0x2491df14
// Total length of the node database header, has magic to verify and type_hash to map to load function // Total length of the node database header, has magic to verify and type_hash to map to load function
NODE_DB_HEADER_LEN = 24 NODE_DB_HEADER_LEN = 28
EXTENSION_DB_HEADER_LEN = 16 EXTENSION_DB_HEADER_LEN = 16
) )
@ -32,6 +37,7 @@ func (id NodeID) MarshalJSON() ([]byte, error) {
str := id.String() str := id.String()
return json.Marshal(&str) return json.Marshal(&str)
} }
func (id *NodeID) UnmarshalJSON(bytes []byte) error { func (id *NodeID) UnmarshalJSON(bytes []byte) error {
var id_str string var id_str string
err := json.Unmarshal(bytes, &id_str) err := json.Unmarshal(bytes, &id_str)
@ -95,6 +101,7 @@ type QueuedSignal struct {
// Default message channel size for nodes // Default message channel size for nodes
// Nodes represent a group of extensions that can be collectively addressed // Nodes represent a group of extensions that can be collectively addressed
type Node struct { type Node struct {
Key *ecdsa.PrivateKey
ID NodeID ID NodeID
Type NodeType Type NodeType
Extensions map[ExtType]Extension Extensions map[ExtType]Extension
@ -268,16 +275,24 @@ func GetExt[T Extension](node *Node) (T, error) {
func (node *Node) Serialize() ([]byte, error) { func (node *Node) Serialize() ([]byte, error) {
extensions := make([]ExtensionDB, len(node.Extensions)) extensions := make([]ExtensionDB, len(node.Extensions))
key_bytes, err := x509.MarshalECPrivateKey(node.Key)
if err != nil {
return nil, err
}
node_db := NodeDB{ node_db := NodeDB{
Header: NodeDBHeader{ Header: NodeDBHeader{
Magic: NODE_DB_MAGIC, Magic: NODE_DB_MAGIC,
TypeHash: Hash(node.Type), TypeHash: Hash(node.Type),
KeyLength: uint32(len(key_bytes)),
BufferSize: node.BufferSize, BufferSize: node.BufferSize,
NumExtensions: uint32(len(extensions)), NumExtensions: uint32(len(extensions)),
NumQueuedSignals: uint32(len(node.SignalQueue)), NumQueuedSignals: uint32(len(node.SignalQueue)),
}, },
Extensions: extensions, Extensions: extensions,
QueuedSignals: node.SignalQueue, QueuedSignals: node.SignalQueue,
KeyBytes: key_bytes,
} }
i := 0 i := 0
@ -299,8 +314,23 @@ func (node *Node) Serialize() ([]byte, error) {
return node_db.Serialize(), nil return node_db.Serialize(), nil
} }
func KeyID(pub *ecdsa.PublicKey) NodeID {
ser := elliptic.Marshal(pub.Curve, pub.X, pub.Y)
str := uuid.NewHash(sha512.New(), ZeroUUID, ser, 3)
return NodeID(str)
}
// Create a new node in memory and start it's event loop // Create a new node in memory and start it's event loop
func NewNode(ctx *Context, id NodeID, node_type NodeType, buffer_size uint32, queued_signals []QueuedSignal, extensions ...Extension) *Node { // TODO: Change panics to errors
func NewNode(ctx *Context, key *ecdsa.PrivateKey, node_type NodeType, buffer_size uint32, queued_signals []QueuedSignal, extensions ...Extension) *Node {
var err error
if key == nil {
key, err = ecdsa.GenerateKey(ctx.ECDSA, rand.Reader)
if err != nil {
panic(err)
}
}
id := KeyID(&key.PublicKey)
_, exists := ctx.Node(id) _, exists := ctx.Node(id)
if exists == true { if exists == true {
panic("Attempted to create an existing node") panic("Attempted to create an existing node")
@ -334,6 +364,7 @@ func NewNode(ctx *Context, id NodeID, node_type NodeType, buffer_size uint32, qu
next_signal, timeout_chan := SoonestSignal(queued_signals) next_signal, timeout_chan := SoonestSignal(queued_signals)
node := &Node{ node := &Node{
Key: key,
ID: id, ID: id,
Type: node_type, Type: node_type,
Extensions: ext_map, Extensions: ext_map,
@ -344,7 +375,7 @@ func NewNode(ctx *Context, id NodeID, node_type NodeType, buffer_size uint32, qu
NextSignal: next_signal, NextSignal: next_signal,
} }
ctx.AddNode(id, node) ctx.AddNode(id, node)
err := WriteNode(ctx, node) err = WriteNode(ctx, node)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -384,6 +415,7 @@ type NodeDBHeader struct {
NumExtensions uint32 NumExtensions uint32
NumQueuedSignals uint32 NumQueuedSignals uint32
BufferSize uint32 BufferSize uint32
KeyLength uint32
TypeHash uint64 TypeHash uint64
} }
@ -391,6 +423,7 @@ type NodeDB struct {
Header NodeDBHeader Header NodeDBHeader
QueuedSignals []QueuedSignal QueuedSignals []QueuedSignal
Extensions []ExtensionDB Extensions []ExtensionDB
KeyBytes []byte
} }
//TODO: add size safety checks //TODO: add size safety checks
@ -403,7 +436,8 @@ func NewNodeDB(data []byte) (NodeDB, error) {
num_extensions := binary.BigEndian.Uint32(data[4:8]) num_extensions := binary.BigEndian.Uint32(data[4:8])
num_queued_signals := binary.BigEndian.Uint32(data[8:12]) num_queued_signals := binary.BigEndian.Uint32(data[8:12])
buffer_size := binary.BigEndian.Uint32(data[12:16]) buffer_size := binary.BigEndian.Uint32(data[12:16])
node_type_hash := binary.BigEndian.Uint64(data[16:24]) key_length := binary.BigEndian.Uint32(data[16:20])
node_type_hash := binary.BigEndian.Uint64(data[20:28])
ptr += NODE_DB_HEADER_LEN ptr += NODE_DB_HEADER_LEN
@ -411,6 +445,14 @@ func NewNodeDB(data []byte) (NodeDB, error) {
return zero, fmt.Errorf("header has incorrect magic 0x%x", magic) return zero, fmt.Errorf("header has incorrect magic 0x%x", magic)
} }
key_bytes := make([]byte, key_length)
n := copy(key_bytes, data[ptr:(ptr+int(key_length))])
if n != int(key_length) {
return zero, fmt.Errorf("not enough key bytes: %d", n)
}
ptr += int(key_length)
extensions := make([]ExtensionDB, num_extensions) extensions := make([]ExtensionDB, num_extensions)
for i, _ := range(extensions) { for i, _ := range(extensions) {
cur := data[ptr:] cur := data[ptr:]
@ -443,9 +485,11 @@ func NewNodeDB(data []byte) (NodeDB, error) {
Magic: magic, Magic: magic,
TypeHash: node_type_hash, TypeHash: node_type_hash,
BufferSize: buffer_size, BufferSize: buffer_size,
KeyLength: key_length,
NumExtensions: num_extensions, NumExtensions: num_extensions,
NumQueuedSignals: num_queued_signals, NumQueuedSignals: num_queued_signals,
}, },
KeyBytes: key_bytes,
Extensions: extensions, Extensions: extensions,
QueuedSignals: queued_signals, QueuedSignals: queued_signals,
}, nil }, nil
@ -461,12 +505,14 @@ func (header NodeDBHeader) Serialize() []byte {
binary.BigEndian.PutUint32(ret[4:8], header.NumExtensions) binary.BigEndian.PutUint32(ret[4:8], header.NumExtensions)
binary.BigEndian.PutUint32(ret[8:12], header.NumQueuedSignals) binary.BigEndian.PutUint32(ret[8:12], header.NumQueuedSignals)
binary.BigEndian.PutUint32(ret[12:16], header.BufferSize) binary.BigEndian.PutUint32(ret[12:16], header.BufferSize)
binary.BigEndian.PutUint64(ret[16:24], header.TypeHash) binary.BigEndian.PutUint32(ret[16:20], header.KeyLength)
binary.BigEndian.PutUint64(ret[20:28], header.TypeHash)
return ret return ret
} }
func (node NodeDB) Serialize() []byte { func (node NodeDB) Serialize() []byte {
ser := node.Header.Serialize() ser := node.Header.Serialize()
ser = append(ser, node.KeyBytes...)
for _, extension := range(node.Extensions) { for _, extension := range(node.Extensions) {
ser = append(ser, extension.Serialize()...) ser = append(ser, extension.Serialize()...)
} }
@ -541,6 +587,20 @@ func LoadNode(ctx * Context, id NodeID) (*Node, error) {
return nil, err return nil, err
} }
key, err := x509.ParseECPrivateKey(node_db.KeyBytes)
if err != nil {
return nil, err
}
if key.PublicKey.Curve != ctx.ECDSA {
return nil, fmt.Errorf("%s - wrong ec curve for private key: %+v, expected %+v", id, key.PublicKey.Curve, ctx.ECDSA)
}
key_id := KeyID(&key.PublicKey)
if key_id != id {
return nil, fmt.Errorf("KeyID(%s) != %s", key_id, id)
}
node_type, known := ctx.Types[node_db.Header.TypeHash] node_type, known := ctx.Types[node_db.Header.TypeHash]
if known == false { if known == false {
return nil, fmt.Errorf("Tried to load node %s of type 0x%x, which is not a known node type", id, node_db.Header.TypeHash) return nil, fmt.Errorf("Tried to load node %s of type 0x%x, which is not a known node type", id, node_db.Header.TypeHash)
@ -548,7 +608,8 @@ func LoadNode(ctx * Context, id NodeID) (*Node, error) {
next_signal, timeout_chan := SoonestSignal(node_db.QueuedSignals) next_signal, timeout_chan := SoonestSignal(node_db.QueuedSignals)
node := &Node{ node := &Node{
ID: id, Key: key,
ID: key_id,
Type: node_type.Type, Type: node_type.Type,
Extensions: map[ExtType]Extension{}, Extensions: map[ExtType]Extension{},
MsgChan: make(chan Msg, node_db.Header.BufferSize), MsgChan: make(chan Msg, node_db.Header.BufferSize),

@ -3,6 +3,8 @@ package graphvent
import ( import (
"testing" "testing"
"time" "time"
"crypto/rand"
"crypto/ecdsa"
) )
func TestNodeDB(t *testing.T) { func TestNodeDB(t *testing.T) {
@ -11,7 +13,7 @@ func TestNodeDB(t *testing.T) {
err := ctx.RegisterNodeType(node_type, []ExtType{GroupExtType}) err := ctx.RegisterNodeType(node_type, []ExtType{GroupExtType})
fatalErr(t, err) fatalErr(t, err)
node := NewNode(ctx, RandID(), node_type, 10, nil, NewGroupExt(nil)) node := NewNode(ctx, nil, node_type, 10, nil, NewGroupExt(nil))
ctx.Nodes = NodeMap{} ctx.Nodes = NodeMap{}
_, err = ctx.GetNode(node.ID) _, err = ctx.GetNode(node.ID)
@ -24,8 +26,13 @@ func TestNodeRead(t *testing.T) {
err := ctx.RegisterNodeType(node_type, []ExtType{ACLExtType, GroupExtType}) err := ctx.RegisterNodeType(node_type, []ExtType{ACLExtType, GroupExtType})
fatalErr(t, err) fatalErr(t, err)
n1_id := RandID() n1_key, err := ecdsa.GenerateKey(ctx.ECDSA, rand.Reader)
n2_id := RandID() fatalErr(t, err)
n2_key, err := ecdsa.GenerateKey(ctx.ECDSA, rand.Reader)
fatalErr(t, err)
n1_id := KeyID(&n1_key.PublicKey)
n2_id := KeyID(&n2_key.PublicKey)
ctx.Log.Logf("test", "N1: %s", n1_id) ctx.Log.Logf("test", "N1: %s", n1_id)
ctx.Log.Logf("test", "N2: %s", n2_id) ctx.Log.Logf("test", "N2: %s", n2_id)
@ -34,12 +41,12 @@ func TestNodeRead(t *testing.T) {
n1_id: Actions{MakeAction(ReadResultSignalType, "+")}, n1_id: Actions{MakeAction(ReadResultSignalType, "+")},
}) })
n2_listener := NewListenerExt(10) n2_listener := NewListenerExt(10)
n2 := NewNode(ctx, n2_id, node_type, 10, nil, NewACLExt(n2_policy), NewGroupExt(nil), n2_listener) n2 := NewNode(ctx, n2_key, node_type, 10, nil, NewACLExt(n2_policy), NewGroupExt(nil), n2_listener)
n1_policy := NewPerNodePolicy(map[NodeID]Actions{ n1_policy := NewPerNodePolicy(map[NodeID]Actions{
n2_id: Actions{MakeAction(ReadSignalType, "+")}, n2_id: Actions{MakeAction(ReadSignalType, "+")},
}) })
n1 := NewNode(ctx, n1_id, node_type, 10, nil, NewACLExt(n1_policy), NewGroupExt(nil)) n1 := NewNode(ctx, n1_key, node_type, 10, nil, NewACLExt(n1_policy), NewGroupExt(nil))
ctx.Send(n2.ID, n1.ID, NewReadSignal(map[ExtType][]string{ ctx.Send(n2.ID, n1.ID, NewReadSignal(map[ExtType][]string{
GroupExtType: []string{"members"}, GroupExtType: []string{"members"},

@ -29,6 +29,7 @@ func (signal_type SignalType) String() string {
type Signal interface { type Signal interface {
Serializable[SignalType] Serializable[SignalType]
Direction() SignalDirection Direction() SignalDirection
MarshalJSON() ([]byte, error)
Permission() Action Permission() Action
} }
@ -49,8 +50,12 @@ func (signal BaseSignal) Direction() SignalDirection {
return signal.SignalDirection return signal.SignalDirection
} }
func (signal BaseSignal) MarshalJSON() ([]byte, error) {
return json.Marshal(signal)
}
func (signal BaseSignal) Serialize() ([]byte, error) { func (signal BaseSignal) Serialize() ([]byte, error) {
return json.MarshalIndent(signal, "", " ") return signal.MarshalJSON()
} }
func NewBaseSignal(signal_type SignalType, direction SignalDirection) BaseSignal { func NewBaseSignal(signal_type SignalType, direction SignalDirection) BaseSignal {
@ -183,3 +188,4 @@ func NewReadResultSignal(exts map[ExtType]map[string]interface{}) ReadResultSign
} }
} }