Improvements to Yggdrasil demo (#1114)

* Improvements to Yggdrasil demo

* Fix missing copyright

* Fix tie-break
This commit is contained in:
Neil Alexander 2020-06-10 16:29:02 +01:00 committed by GitHub
parent 90a0aa9b3e
commit 3b4be90000
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 217 additions and 17 deletions

View File

@ -0,0 +1,53 @@
// Copyright 2019 Google LLC
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
//
// Original code from https://github.com/FiloSottile/age/blob/bbab440e198a4d67ba78591176c7853e62d29e04/internal/age/ssh.go
package convert
import (
"crypto/ed25519"
"crypto/sha512"
"math/big"
"golang.org/x/crypto/curve25519"
)
var curve25519P, _ = new(big.Int).SetString("57896044618658097711785492504343953926634992332820282019728792003956564819949", 10)
func Ed25519PrivateKeyToCurve25519(pk ed25519.PrivateKey) []byte {
h := sha512.New()
_, _ = h.Write(pk.Seed())
out := h.Sum(nil)
return out[:curve25519.ScalarSize]
}
func Ed25519PublicKeyToCurve25519(pk ed25519.PublicKey) []byte {
// ed25519.PublicKey is a little endian representation of the y-coordinate,
// with the most significant bit set based on the sign of the x-coordinate.
bigEndianY := make([]byte, ed25519.PublicKeySize)
for i, b := range pk {
bigEndianY[ed25519.PublicKeySize-i-1] = b
}
bigEndianY[0] &= 0b0111_1111
// The Montgomery u-coordinate is derived through the bilinear map
// u = (1 + y) / (1 - y)
// See https://blog.filippo.io/using-ed25519-keys-for-encryption.
y := new(big.Int).SetBytes(bigEndianY)
denom := big.NewInt(1)
denom.ModInverse(denom.Sub(denom, y), curve25519P) // 1 / (1 - y)
u := y.Mul(y.Add(y, big.NewInt(1)), denom)
u.Mod(u, curve25519P)
out := make([]byte, curve25519.PointSize)
uBytes := u.Bytes()
for i, b := range uBytes {
out[len(uBytes)-i-1] = b
}
return out
}

View File

@ -0,0 +1,51 @@
// Copyright 2020 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package convert
import (
"bytes"
"crypto/ed25519"
"encoding/hex"
"testing"
"golang.org/x/crypto/curve25519"
)
func TestKeyConversion(t *testing.T) {
edPub, edPriv, err := ed25519.GenerateKey(nil)
if err != nil {
t.Fatal(err)
}
t.Log("Signing public:", hex.EncodeToString(edPub))
t.Log("Signing private:", hex.EncodeToString(edPriv))
cuPriv := Ed25519PrivateKeyToCurve25519(edPriv)
t.Log("Encryption private:", hex.EncodeToString(cuPriv))
cuPub := Ed25519PublicKeyToCurve25519(edPub)
t.Log("Converted encryption public:", hex.EncodeToString(cuPub))
var realPub, realPriv [32]byte
copy(realPriv[:32], cuPriv[:32])
curve25519.ScalarBaseMult(&realPub, &realPriv)
t.Log("Scalar-multed encryption public:", hex.EncodeToString(realPub[:]))
if !bytes.Equal(realPriv[:], cuPriv[:]) {
t.Fatal("Private keys should be equal (this means the test is broken)")
}
if !bytes.Equal(realPub[:], cuPub[:]) {
t.Fatal("Public keys should be equal")
}
}

View File

@ -16,7 +16,9 @@ package main
import ( import (
"context" "context"
"crypto/ed25519"
"crypto/tls" "crypto/tls"
"encoding/hex"
"flag" "flag"
"fmt" "fmt"
"net" "net"
@ -25,6 +27,8 @@ import (
"time" "time"
"github.com/matrix-org/dendrite/appservice" "github.com/matrix-org/dendrite/appservice"
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/convert"
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggconn" "github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggconn"
"github.com/matrix-org/dendrite/eduserver" "github.com/matrix-org/dendrite/eduserver"
"github.com/matrix-org/dendrite/eduserver/cache" "github.com/matrix-org/dendrite/eduserver/cache"
@ -35,7 +39,6 @@ import (
"github.com/matrix-org/dendrite/internal/setup" "github.com/matrix-org/dendrite/internal/setup"
"github.com/matrix-org/dendrite/publicroomsapi/storage" "github.com/matrix-org/dendrite/publicroomsapi/storage"
"github.com/matrix-org/dendrite/roomserver" "github.com/matrix-org/dendrite/roomserver"
"github.com/matrix-org/dendrite/serverkeyapi"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -61,7 +64,13 @@ func createFederationClient(
) *gomatrixserverlib.FederationClient { ) *gomatrixserverlib.FederationClient {
yggdialer := func(_, address string) (net.Conn, error) { yggdialer := func(_, address string) (net.Conn, error) {
tokens := strings.Split(address, ":") tokens := strings.Split(address, ":")
return n.Dial("curve25519", tokens[0]) raw, err := hex.DecodeString(tokens[0])
if err != nil {
return nil, fmt.Errorf("hex.DecodeString: %w", err)
}
converted := convert.Ed25519PublicKeyToCurve25519(ed25519.PublicKey(raw))
convhex := hex.EncodeToString(converted)
return n.Dial("curve25519", convhex)
} }
yggdialerctx := func(ctx context.Context, network, address string) (net.Conn, error) { yggdialerctx := func(ctx context.Context, network, address string) (net.Conn, error) {
return yggdialer(network, address) return yggdialer(network, address)
@ -102,9 +111,9 @@ func main() {
cfg := &config.Dendrite{} cfg := &config.Dendrite{}
cfg.SetDefaults() cfg.SetDefaults()
cfg.Matrix.ServerName = gomatrixserverlib.ServerName(ygg.EncryptionPublicKey()) cfg.Matrix.ServerName = gomatrixserverlib.ServerName(ygg.DerivedServerName())
cfg.Matrix.PrivateKey = ygg.SigningPrivateKey() cfg.Matrix.PrivateKey = ygg.SigningPrivateKey()
cfg.Matrix.KeyID = gomatrixserverlib.KeyID("ed25519:auto") cfg.Matrix.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
cfg.Kafka.UseNaffka = true cfg.Kafka.UseNaffka = true
cfg.Kafka.Topics.OutputRoomEvent = "roomserverOutput" cfg.Kafka.Topics.OutputRoomEvent = "roomserverOutput"
cfg.Kafka.Topics.OutputClientData = "clientapiOutput" cfg.Kafka.Topics.OutputClientData = "clientapiOutput"
@ -130,9 +139,7 @@ func main() {
deviceDB := base.CreateDeviceDB() deviceDB := base.CreateDeviceDB()
federation := createFederationClient(base, ygg) federation := createFederationClient(base, ygg)
serverKeyAPI := serverkeyapi.NewInternalAPI( serverKeyAPI := &signing.YggdrasilKeys{}
base.Cfg, federation, base.Caches,
)
keyRing := serverKeyAPI.KeyRing() keyRing := serverKeyAPI.KeyRing()
rsComponent := roomserver.NewInternalAPI( rsComponent := roomserver.NewInternalAPI(
@ -170,7 +177,7 @@ func main() {
EDUInternalAPI: eduInputAPI, EDUInternalAPI: eduInputAPI,
FederationSenderAPI: fsAPI, FederationSenderAPI: fsAPI,
RoomserverAPI: rsAPI, RoomserverAPI: rsAPI,
ServerKeyAPI: serverKeyAPI, //ServerKeyAPI: serverKeyAPI,
PublicRoomsDB: publicRoomsDB, PublicRoomsDB: publicRoomsDB,
} }
@ -185,7 +192,7 @@ func main() {
) )
go func() { go func() {
logrus.Info("Listening on ", ygg.EncryptionPublicKey()) logrus.Info("Listening on ", ygg.DerivedServerName())
logrus.Fatal(httpServer.Serve(ygg)) logrus.Fatal(httpServer.Serve(ygg))
}() }()
go func() { go func() {

View File

@ -0,0 +1,69 @@
// Copyright 2020 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package signing
import (
"context"
"encoding/hex"
"fmt"
"time"
"github.com/matrix-org/gomatrixserverlib"
)
const KeyID = "ed25519:dendrite-demo-yggdrasil"
type YggdrasilKeys struct {
}
func (f *YggdrasilKeys) KeyRing() *gomatrixserverlib.KeyRing {
return &gomatrixserverlib.KeyRing{
KeyDatabase: f,
}
}
func (f *YggdrasilKeys) FetchKeys(
ctx context.Context,
requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
) (map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult, error) {
res := make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult)
for req := range requests {
if req.KeyID != KeyID {
return nil, fmt.Errorf("FetchKeys: cannot fetch key with ID %s, should be %s", req.KeyID, KeyID)
}
hexkey, err := hex.DecodeString(string(req.ServerName))
if err != nil {
return nil, fmt.Errorf("FetchKeys: can't decode server name %q: %w", req.ServerName, err)
}
res[req] = gomatrixserverlib.PublicKeyLookupResult{
VerifyKey: gomatrixserverlib.VerifyKey{
Key: hexkey,
},
ExpiredTS: gomatrixserverlib.PublicKeyNotExpired,
ValidUntilTS: gomatrixserverlib.AsTimestamp(time.Now().Add(24 * time.Hour * 365)),
}
}
return res, nil
}
func (f *YggdrasilKeys) FetcherName() string {
return "YggdrasilKeys"
}
func (f *YggdrasilKeys) StoreKeys(ctx context.Context, results map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult) error {
return nil
}

View File

@ -24,6 +24,8 @@ import (
"os" "os"
"sync" "sync"
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/convert"
"github.com/libp2p/go-yamux" "github.com/libp2p/go-yamux"
yggdrasiladmin "github.com/yggdrasil-network/yggdrasil-go/src/admin" yggdrasiladmin "github.com/yggdrasil-network/yggdrasil-go/src/admin"
yggdrasilconfig "github.com/yggdrasil-network/yggdrasil-go/src/config" yggdrasilconfig "github.com/yggdrasil-network/yggdrasil-go/src/config"
@ -56,8 +58,6 @@ func Setup(instanceName, instancePeer string) (*Node, error) {
log: gologme.New(os.Stdout, "YGG ", log.Flags()), log: gologme.New(os.Stdout, "YGG ", log.Flags()),
incoming: make(chan *yamux.Stream), incoming: make(chan *yamux.Stream),
} }
n.config.AdminListen = fmt.Sprintf("unix://./%s-yggdrasil.sock", instanceName)
n.config.MulticastInterfaces = []string{".*"}
yggfile := fmt.Sprintf("%s-yggdrasil.conf", instanceName) yggfile := fmt.Sprintf("%s-yggdrasil.conf", instanceName)
if _, err := os.Stat(yggfile); !os.IsNotExist(err) { if _, err := os.Stat(yggfile); !os.IsNotExist(err) {
@ -69,6 +69,11 @@ func Setup(instanceName, instancePeer string) (*Node, error) {
panic(err) panic(err)
} }
} else { } else {
n.config.AdminListen = fmt.Sprintf("unix://./%s-yggdrasil.sock", instanceName)
n.config.MulticastInterfaces = []string{".*"}
n.config.EncryptionPrivateKey = hex.EncodeToString(n.EncryptionPrivateKey())
n.config.EncryptionPublicKey = hex.EncodeToString(n.EncryptionPublicKey())
j, err := json.MarshalIndent(n.config, "", " ") j, err := json.MarshalIndent(n.config, "", " ")
if err != nil { if err != nil {
panic(err) panic(err)
@ -119,8 +124,23 @@ func Setup(instanceName, instancePeer string) (*Node, error) {
return n, nil return n, nil
} }
func (n *Node) EncryptionPublicKey() string { func (n *Node) DerivedServerName() string {
return n.core.EncryptionPublicKey() return hex.EncodeToString(n.SigningPublicKey())
}
func (n *Node) EncryptionPublicKey() []byte {
edkey := n.SigningPublicKey()
return convert.Ed25519PublicKeyToCurve25519(edkey)
}
func (n *Node) EncryptionPrivateKey() []byte {
edkey := n.SigningPrivateKey()
return convert.Ed25519PrivateKeyToCurve25519(edkey)
}
func (n *Node) SigningPublicKey() ed25519.PublicKey {
pubBytes, _ := hex.DecodeString(n.config.SigningPublicKey)
return ed25519.PublicKey(pubBytes)
} }
func (n *Node) SigningPrivateKey() ed25519.PrivateKey { func (n *Node) SigningPrivateKey() ed25519.PrivateKey {

View File

@ -40,10 +40,10 @@ func (n *Node) listenFromYgg() {
return return
} }
var session *yamux.Session var session *yamux.Session
if strings.Compare(n.EncryptionPublicKey(), conn.RemoteAddr().String()) < 0 { if strings.Compare(conn.RemoteAddr().String(), n.DerivedServerName()) < 0 {
session, err = yamux.Client(conn, n.yamuxConfig())
} else {
session, err = yamux.Server(conn, n.yamuxConfig()) session, err = yamux.Server(conn, n.yamuxConfig())
} else {
session, err = yamux.Client(conn, n.yamuxConfig())
} }
if err != nil { if err != nil {
return return
@ -96,7 +96,7 @@ func (n *Node) DialContext(ctx context.Context, network, address string) (net.Co
n.log.Println("n.dialer.DialContext:", err) n.log.Println("n.dialer.DialContext:", err)
return nil, err return nil, err
} }
if strings.Compare(n.EncryptionPublicKey(), address) < 0 { if strings.Compare(address, n.DerivedServerName()) > 0 {
session, err = yamux.Client(conn, n.yamuxConfig()) session, err = yamux.Client(conn, n.yamuxConfig())
} else { } else {
session, err = yamux.Server(conn, n.yamuxConfig()) session, err = yamux.Server(conn, n.yamuxConfig())