diff --git a/packages/autopeering/peer/local.go b/packages/autopeering/peer/local.go index 32a7a9dfbecf74bccd7a3919f214b964caadd670..19595a32cc5244806a12b1394bff342dc9f1dc58 100644 --- a/packages/autopeering/peer/local.go +++ b/packages/autopeering/peer/local.go @@ -42,13 +42,25 @@ func newLocal(key PrivateKey, serviceRecord *service.Record, db DB) *Local { } // NewLocal creates a new local peer linked to the provided db. -func NewLocal(network string, address string, db DB) (*Local, error) { - key, err := db.LocalPrivateKey() - if err != nil { - return nil, err +// If an optional seed is provided, the seed is used to generate the private key. Without a seed, +// the provided key is loaded from the provided database and generated if not stored there. +func NewLocal(network string, address string, db DB, seed ...[]byte) (*Local, error) { + var key PrivateKey + if len(seed) > 0 { + key = PrivateKey(ed25519.NewKeyFromSeed(seed[0])) + if err := db.UpdateLocalPrivateKey(key); err != nil { + return nil, err + } + } else { + var err error + key, err = db.LocalPrivateKey() + if err != nil { + return nil, err + } } + if l := len(key); l != ed25519.PrivateKeySize { - return nil, fmt.Errorf("invalid key length: %d, need %d", l, ed25519.PublicKeySize) + return nil, fmt.Errorf("invalid key length: %d, need %d", l, ed25519.PrivateKeySize) } // update the external address used for the peering serviceRecord := service.New() diff --git a/packages/autopeering/peer/mapdb.go b/packages/autopeering/peer/mapdb.go index df3ead3f55b623bb611b6dc291ea4cd2a3ec57ba..a3ebbb1a1aa45c420565fe353ddc5a95722d2fba 100644 --- a/packages/autopeering/peer/mapdb.go +++ b/packages/autopeering/peer/mapdb.go @@ -67,6 +67,15 @@ func (db *mapDB) LocalPrivateKey() (PrivateKey, error) { return db.key, nil } +// UpdateLocalPrivateKey stores the provided key in the database. +func (db *mapDB) UpdateLocalPrivateKey(key PrivateKey) error { + db.mutex.Lock() + defer db.mutex.Unlock() + + db.key = key + return nil +} + // LastPing returns that property for the given peer ID and address. func (db *mapDB) LastPing(id ID, address string) time.Time { db.mutex.RLock() diff --git a/packages/autopeering/peer/peerdb.go b/packages/autopeering/peer/peerdb.go index 106517aa56ef0562b3d018529644394e8cd30c8d..def6344b4f270d140c121e50a5504cb0101b5aba 100644 --- a/packages/autopeering/peer/peerdb.go +++ b/packages/autopeering/peer/peerdb.go @@ -28,6 +28,8 @@ const ( type DB interface { // LocalPrivateKey returns the private key stored in the database or creates a new one. LocalPrivateKey() (PrivateKey, error) + // UpdateLocalPrivateKey stores the provided key in the database. + UpdateLocalPrivateKey(key PrivateKey) error // Peer retrieves a peer from the database. Peer(id ID) *Peer @@ -175,7 +177,7 @@ func (db *persistentDB) LocalPrivateKey() (PrivateKey, error) { if err == database.ErrKeyNotFound { key, err = generatePrivateKey() if err == nil { - err = db.db.Set(localFieldKey(dbLocalKey), key) + err = db.UpdateLocalPrivateKey(key) } return key, err } @@ -186,6 +188,11 @@ func (db *persistentDB) LocalPrivateKey() (PrivateKey, error) { return key, nil } +// UpdateLocalPrivateKey stores the provided key in the database. +func (db *persistentDB) UpdateLocalPrivateKey(key PrivateKey) error { + return db.db.Set(localFieldKey(dbLocalKey), key) +} + // LastPing returns that property for the given peer ID and address. func (db *persistentDB) LastPing(id ID, address string) time.Time { return time.Unix(db.getInt64(nodeFieldKey(id, address, dbNodePing)), 0) diff --git a/plugins/autopeering/local/local.go b/plugins/autopeering/local/local.go index 71e358c0f9d0d9faf6fbaaf4cedc704d551cad01..ac43be56d9de48ca561fdccc11a240cf27fe75d8 100644 --- a/plugins/autopeering/local/local.go +++ b/plugins/autopeering/local/local.go @@ -1,6 +1,8 @@ package local import ( + "crypto/ed25519" + "encoding/base64" "fmt" "io/ioutil" "net" @@ -23,7 +25,7 @@ func configureLocal() *peer.Local { ip := net.ParseIP(parameter.NodeConfig.GetString(CFG_ADDRESS)) if ip == nil { - log.Fatalf("Invalid IP address: %s", parameter.NodeConfig.GetString(CFG_ADDRESS)) + log.Fatalf("Invalid %s address: %s", CFG_ADDRESS, parameter.NodeConfig.GetString(CFG_ADDRESS)) } if ip.IsUnspecified() { log.Info("Querying public IP ...") @@ -40,7 +42,25 @@ func configureLocal() *peer.Local { // create a new local node db := peer.NewPersistentDB(log) - local, err := peer.NewLocal("udp", net.JoinHostPort(ip.String(), port), db) + // the private key seed of the current local can be returned the following way: + // key, _ := db.LocalPrivateKey() + // fmt.Println(base64.StdEncoding.EncodeToString(ed25519.PrivateKey(key).Seed())) + + // set the private key from the seed provided in the config + var seed [][]byte + if parameter.NodeConfig.IsSet(CFG_SEED) { + str := parameter.NodeConfig.GetString(CFG_SEED) + bytes, err := base64.StdEncoding.DecodeString(str) + if err != nil { + log.Fatalf("Invalid %s: %s", CFG_SEED, err) + } + if l := len(bytes); l != ed25519.SeedSize { + log.Fatalf("Invalid %s length: %d, need %d", CFG_SEED, l, ed25519.SeedSize) + } + seed = append(seed, bytes) + } + + local, err := peer.NewLocal("udp", net.JoinHostPort(ip.String(), port), db, seed...) if err != nil { log.Fatalf("Error creating local: %s", err) } diff --git a/plugins/autopeering/local/parameters.go b/plugins/autopeering/local/parameters.go index 1bb7847d5b2c100fc5e0fe1032b8acbe6ae19c87..ab7629e83418fa195453b14edb1d9915c21e079e 100644 --- a/plugins/autopeering/local/parameters.go +++ b/plugins/autopeering/local/parameters.go @@ -7,9 +7,11 @@ import ( const ( CFG_ADDRESS = "autopeering.address" CFG_PORT = "autopeering.port" + CFG_SEED = "autopeering.seed" ) func init() { flag.String(CFG_ADDRESS, "0.0.0.0", "address to bind for incoming peering requests") flag.Int(CFG_PORT, 14626, "udp port for incoming peering requests") + flag.BytesBase64(CFG_SEED, nil, "private key seed used to derive the node identity; optional Base64 encoded 256-bit string") }