mirror of
https://git.numenor-labs.us/dsfx.git
synced 2025-04-29 16:20:34 +00:00
switch ecdsa to ed25519
This commit is contained in:
parent
9eaf838069
commit
ff81b1bc3e
14
README.md
14
README.md
@ -6,7 +6,7 @@ dsfx is a robust, secure, and distributed file exchange system written in Go. De
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **End-to-End Security:** Uses modern cryptographic primitives (ECDSA keys, for example) to ensure that all file exchanges are encrypted and authenticated.
|
- **End-to-End Security:** Uses modern cryptographic primitives (ED25519 keys, for example) to ensure that all file exchanges are encrypted and authenticated.
|
||||||
- **Distributed Architecture:** Designed for secure file exchange across multiple nodes with built-in support for key-based authentication.
|
- **Distributed Architecture:** Designed for secure file exchange across multiple nodes with built-in support for key-based authentication.
|
||||||
- **High Performance:** Optimized for low latency and high throughput, with a focus on reliable and predictable behavior.
|
- **High Performance:** Optimized for low latency and high throughput, with a focus on reliable and predictable behavior.
|
||||||
- **Administrative and Test Tools:** The dsfx client can be used to test connectivity and perform preliminary administrative actions against the dsfx server.
|
- **Administrative and Test Tools:** The dsfx client can be used to test connectivity and perform preliminary administrative actions against the dsfx server.
|
||||||
@ -48,13 +48,13 @@ go install ./cmd/...
|
|||||||
|
|
||||||
### Starting the Server
|
### Starting the Server
|
||||||
|
|
||||||
The dsfxnode requires a listening host, port, and an identity key (ECDSA private key in PEM format). For example:
|
The dsfxnode requires a listening host, port, and an identity key (ED25519 private key in Base64 format). For example:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
dsfxnode -host localhost -port 8000 -key /path/to/serverkey.pem
|
dsfxnode -host localhost -port 8000 -key /path/to/serverkey
|
||||||
```
|
```
|
||||||
|
|
||||||
> Note, if you need to generate a new ECDSA key, you can use the following command: `go run ./tool/genkey > path/to/masterkey.pem`
|
> Note, if you need to generate a new ED25519 key, you can use the following command: `go run ./tool/genkey > path/to/masterkey`
|
||||||
|
|
||||||
Command-line flags for dsfx-server:
|
Command-line flags for dsfx-server:
|
||||||
|
|
||||||
@ -65,18 +65,18 @@ The host interface on which the server will listen.
|
|||||||
The TCP port on which the server will accept connections.
|
The TCP port on which the server will accept connections.
|
||||||
|
|
||||||
-key (required)
|
-key (required)
|
||||||
File path to the PEM-encoded ECDSA private key that serves as the server’s master key.
|
File path to the Base64-encoded ED25519 private key that serves as the server’s master key.
|
||||||
|
|
||||||
Once started, the server will bind to the specified host and port and wait for incoming secure file exchange (or other test) connections. When a client connects, the initial payload (up to 1024 bytes) from the client is read and logged.
|
Once started, the server will bind to the specified host and port and wait for incoming secure file exchange (or other test) connections. When a client connects, the initial payload (up to 1024 bytes) from the client is read and logged.
|
||||||
|
|
||||||
### Running the Admin Client
|
### Running the Admin Client
|
||||||
|
|
||||||
The dsfxctl uses a private key for the client (also an ECDSA key in PEM format) and currently supports only the “test” command for checking connectivity to the server.
|
The dsfxctl uses a private key for the client (also an ED25519 key in Base64 format) and currently supports only the “test” command for checking connectivity to the server.
|
||||||
|
|
||||||
Client command usage:
|
Client command usage:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
dsfxctl -key /path/to/clientkey.pem test <remote_addr>
|
dsfxctl -key /path/to/clientkey test <remote_addr>
|
||||||
```
|
```
|
||||||
|
|
||||||
Where:
|
Where:
|
||||||
|
@ -2,7 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ed25519"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
@ -53,7 +53,7 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
identity, err := identity.LoadSigningKeyFromFile(*flagKey)
|
id, err := identity.LoadSigningKeyFromFile(*flagKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ErrorContext(ctx, "failed to load private key", slog.Any("error", err))
|
logger.ErrorContext(ctx, "failed to load private key", slog.Any("error", err))
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -62,7 +62,7 @@ func main() {
|
|||||||
laddr := network.NewAddr(
|
laddr := network.NewAddr(
|
||||||
net.ParseIP("0.0.0.0"),
|
net.ParseIP("0.0.0.0"),
|
||||||
0, // port 0 means any available port
|
0, // port 0 means any available port
|
||||||
&identity.PublicKey,
|
identity.ToPublicKey(id),
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.DebugContext(ctx, "using addr", slog.String("address", laddr.String()))
|
logger.DebugContext(ctx, "using addr", slog.String("address", laddr.String()))
|
||||||
@ -74,7 +74,7 @@ func main() {
|
|||||||
logger.ErrorContext(ctx, "no remote address provided")
|
logger.ErrorContext(ctx, "no remote address provided")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
testConnection(ctx, identity, laddr, raddrRaw)
|
testConnection(ctx, id, laddr, raddrRaw)
|
||||||
case "":
|
case "":
|
||||||
logger.InfoContext(ctx, "no command provided")
|
logger.InfoContext(ctx, "no command provided")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -84,7 +84,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testConnection(ctx context.Context, identity *ecdsa.PrivateKey, laddr *network.Addr, raddrRaw string) {
|
func testConnection(ctx context.Context, id ed25519.PrivateKey, laddr *network.Addr, raddrRaw string) {
|
||||||
logger := logging.FromContext(context.Background())
|
logger := logging.FromContext(context.Background())
|
||||||
|
|
||||||
raddr, err := network.ParseAddr(raddrRaw)
|
raddr, err := network.ParseAddr(raddrRaw)
|
||||||
@ -93,7 +93,7 @@ func testConnection(ctx context.Context, identity *ecdsa.PrivateKey, laddr *netw
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := network.Dial(ctx, identity, laddr, raddr)
|
conn, err := network.Dial(ctx, id, laddr, raddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ErrorContext(ctx, "failed to connect", slog.Any("error", err))
|
logger.ErrorContext(ctx, "failed to connect", slog.Any("error", err))
|
||||||
return
|
return
|
||||||
|
@ -60,7 +60,7 @@ func main() {
|
|||||||
slog.ErrorContext(ctx, "invalid host or port")
|
slog.ErrorContext(ctx, "invalid host or port")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
addr := network.FromTCPAddr(tcpAddr, &masterKey.PublicKey)
|
addr := network.FromTCPAddr(tcpAddr, identity.ToPublicKey(masterKey))
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Listener
|
// Listener
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
package identity
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// DefaultSigningCurve is the default elliptic curve used for signing.
|
|
||||||
DefaultSigningCurve = elliptic.P384
|
|
||||||
|
|
||||||
ExportedPublicKeySize = 215
|
|
||||||
)
|
|
||||||
|
|
||||||
func LoadSigningKeyFromFile(filePath string) (*ecdsa.PrivateKey, error) {
|
|
||||||
masterKeyFile, err := os.ReadFile(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// The second argument is not an error.
|
|
||||||
derEncoded, _ := pem.Decode(masterKeyFile)
|
|
||||||
if derEncoded == nil {
|
|
||||||
return nil, fmt.Errorf("failed to decode master key file")
|
|
||||||
}
|
|
||||||
|
|
||||||
masterKey, err := x509.ParseECPrivateKey(derEncoded.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return masterKey, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate generates a new ECDSA private key for signing.
|
|
||||||
func Generate() (*ecdsa.PrivateKey, error) {
|
|
||||||
return ecdsa.GenerateKey(DefaultSigningCurve(), rand.Reader)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign signs the data with the private key.
|
|
||||||
func Sign(priv *ecdsa.PrivateKey, data []byte) ([]byte, error) {
|
|
||||||
return ecdsa.SignASN1(rand.Reader, priv, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify verifies the signature of the data with the public key.
|
|
||||||
func Verify(pub *ecdsa.PublicKey, data, signature []byte) bool {
|
|
||||||
return ecdsa.VerifyASN1(pub, data, signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportPrivateKey exports the private key as a byte slice.
|
|
||||||
func ExportPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) {
|
|
||||||
der, err := x509.MarshalECPrivateKey(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "PRIVATE KEY",
|
|
||||||
Bytes: der,
|
|
||||||
}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportPublicKey exports the public key as a byte slice.
|
|
||||||
func ExportPublicKey(key *ecdsa.PublicKey) ([]byte, error) {
|
|
||||||
der, err := x509.MarshalPKIXPublicKey(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "PUBLIC KEY",
|
|
||||||
Bytes: der,
|
|
||||||
}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImportPrivateKey imports the private key from a byte slice.
|
|
||||||
func ImportPrivateKey(keyBytes []byte) (*ecdsa.PrivateKey, error) {
|
|
||||||
block, _ := pem.Decode(keyBytes)
|
|
||||||
if block == nil {
|
|
||||||
return nil, fmt.Errorf("failed to decode private key")
|
|
||||||
}
|
|
||||||
|
|
||||||
privKey, err := x509.ParseECPrivateKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return privKey, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImportPublicKey imports the public key from a byte slice.
|
|
||||||
func ImportPublicKey(keyBytes []byte) (*ecdsa.PublicKey, error) {
|
|
||||||
block, _ := pem.Decode(keyBytes)
|
|
||||||
if block == nil {
|
|
||||||
return nil, fmt.Errorf("failed to decode public key")
|
|
||||||
}
|
|
||||||
|
|
||||||
pubKeyAny, err := x509.ParsePKIXPublicKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
pubKey, ok := pubKeyAny.(*ecdsa.PublicKey)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("not an ECDSA public key")
|
|
||||||
}
|
|
||||||
|
|
||||||
return pubKey, nil
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
package identity_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"koti.casa/numenor-labs/dsfx/pkg/crypto/identity"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestImportExportPrivate(t *testing.T) {
|
|
||||||
key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to generate key: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
exported, err := identity.ExportPrivateKey(key)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to export key: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
imported, err := identity.ImportPrivateKey(exported)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to import key: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !key.Equal(imported) {
|
|
||||||
t.Fatalf("imported key does not match original")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestImportExportPublic(t *testing.T) {
|
|
||||||
key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to generate key: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
exported, err := identity.ExportPublicKey(&key.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to export key: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
imported, err := identity.ImportPublicKey(exported)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to import key: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !key.PublicKey.Equal(imported) {
|
|
||||||
t.Fatalf("imported key does not match original")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
91
pkg/crypto/identity/ed25519.go
Normal file
91
pkg/crypto/identity/ed25519.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package identity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ExportedPublicKeySize = 32
|
||||||
|
)
|
||||||
|
|
||||||
|
func LoadSigningKeyFromFile(filePath string) (ed25519.PrivateKey, error) {
|
||||||
|
keyBase64, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the base64-encoded master key file.
|
||||||
|
keyRaw, err := base64.StdEncoding.DecodeString(string(keyBase64))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode master key file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(keyRaw) != ed25519.PrivateKeySize {
|
||||||
|
return nil, fmt.Errorf("invalid master key file size: %d", len(keyRaw))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ed25519.PrivateKey(keyRaw), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate generates a new ED25519 private key for signing.
|
||||||
|
func Generate() (ed25519.PrivateKey, error) {
|
||||||
|
_, key, err := ed25519.GenerateKey(rand.Reader)
|
||||||
|
return key, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign signs the data with the private key.
|
||||||
|
func Sign(priv ed25519.PrivateKey, data []byte) ([]byte, error) {
|
||||||
|
return ed25519.Sign(priv, data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify verifies the signature of the data with the public key.
|
||||||
|
func Verify(pub ed25519.PublicKey, data, signature []byte) bool {
|
||||||
|
return ed25519.Verify(pub, data, signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportPrivateKey exports the private key as a byte slice.
|
||||||
|
func ExportPrivateKey(key ed25519.PrivateKey) ([]byte, error) {
|
||||||
|
return []byte(base64.StdEncoding.EncodeToString(key)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportPublicKey exports the public key as a byte slice.
|
||||||
|
func ExportPublicKey(key ed25519.PublicKey) ([]byte, error) {
|
||||||
|
log.Println("exporting key", len(key))
|
||||||
|
encoded := []byte(base64.StdEncoding.EncodeToString(key))
|
||||||
|
log.Println("exported key", len(encoded))
|
||||||
|
return encoded, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportPrivateKey imports the private key from a byte slice.
|
||||||
|
func ImportPrivateKey(keyBytes []byte) (ed25519.PrivateKey, error) {
|
||||||
|
rawKey, err := base64.StdEncoding.DecodeString(string(keyBytes))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode private key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rawKey) != ed25519.PrivateKeySize {
|
||||||
|
return nil, fmt.Errorf("invalid private key size: %d", len(rawKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ed25519.PrivateKey(rawKey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportPublicKey imports the public key from a byte slice.
|
||||||
|
func ImportPublicKey(keyBytes []byte) (ed25519.PublicKey, error) {
|
||||||
|
log.Println("importing key", len(keyBytes))
|
||||||
|
decoded, err := base64.StdEncoding.DecodeString(string(keyBytes))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode public key: %w", err)
|
||||||
|
}
|
||||||
|
log.Println("imported key", len(decoded))
|
||||||
|
return ed25519.PublicKey(decoded), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToPublicKey(key ed25519.PrivateKey) ed25519.PublicKey {
|
||||||
|
return key.Public().(ed25519.PublicKey)
|
||||||
|
}
|
@ -4,10 +4,11 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdh"
|
"crypto/ecdh"
|
||||||
"crypto/ecdsa"
|
"crypto/ed25519"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
||||||
"koti.casa/numenor-labs/dsfx/pkg/assert"
|
"koti.casa/numenor-labs/dsfx/pkg/assert"
|
||||||
@ -28,8 +29,8 @@ const (
|
|||||||
func Initiate(
|
func Initiate(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn io.ReadWriteCloser,
|
conn io.ReadWriteCloser,
|
||||||
lPrivKey *ecdsa.PrivateKey,
|
lPrivKey ed25519.PrivateKey,
|
||||||
rPubKey *ecdsa.PublicKey,
|
rPubKey ed25519.PublicKey,
|
||||||
) ([]byte, error) {
|
) ([]byte, error) {
|
||||||
logger := logging.FromContext(ctx).WithGroup("handshake")
|
logger := logging.FromContext(ctx).WithGroup("handshake")
|
||||||
|
|
||||||
@ -88,7 +89,7 @@ func Initiate(
|
|||||||
|
|
||||||
// Export the public key of the actor's signing key.
|
// Export the public key of the actor's signing key.
|
||||||
logger.DebugContext(ctx, "exporting public signing key")
|
logger.DebugContext(ctx, "exporting public signing key")
|
||||||
ourPublicKeyRaw, err := identity.ExportPublicKey(&lPrivKey.PublicKey)
|
ourPublicKeyRaw, err := identity.ExportPublicKey(identity.ToPublicKey(lPrivKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -127,6 +128,9 @@ func Initiate(
|
|||||||
}
|
}
|
||||||
assert.Assert(len(derivedKey) == 32, "invalid shared secret size")
|
assert.Assert(len(derivedKey) == 32, "invalid shared secret size")
|
||||||
|
|
||||||
|
log.Println("raw pub key", len(ourPublicKeyRaw))
|
||||||
|
log.Println("raw sig", len(signature))
|
||||||
|
|
||||||
plaintext := make([]byte, 0, len(ourPublicKeyRaw)+len(signature))
|
plaintext := make([]byte, 0, len(ourPublicKeyRaw)+len(signature))
|
||||||
plaintext = append(plaintext, ourPublicKeyRaw...)
|
plaintext = append(plaintext, ourPublicKeyRaw...)
|
||||||
plaintext = append(plaintext, signature...)
|
plaintext = append(plaintext, signature...)
|
||||||
@ -138,6 +142,8 @@ func Initiate(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Println(string(plaintext), len(plaintext))
|
||||||
|
|
||||||
// Write the boxed message to the connection.
|
// Write the boxed message to the connection.
|
||||||
logger.DebugContext(ctx, "sending authentication message", slog.Int("message.size", len(boxedMsg)))
|
logger.DebugContext(ctx, "sending authentication message", slog.Int("message.size", len(boxedMsg)))
|
||||||
boxedMsgPrepared, err := buffer.NewLenPrefixed(boxedMsg)
|
boxedMsgPrepared, err := buffer.NewLenPrefixed(boxedMsg)
|
||||||
@ -203,7 +209,7 @@ func Initiate(
|
|||||||
|
|
||||||
// Accept accepts a handshake from the given actor and connection. It
|
// Accept accepts a handshake from the given actor and connection. It
|
||||||
// returns the shared secret between the actor and the remote actor.
|
// returns the shared secret between the actor and the remote actor.
|
||||||
func Accept(ctx context.Context, conn io.ReadWriteCloser, lPrivKey *ecdsa.PrivateKey) (*ecdsa.PublicKey, []byte, error) {
|
func Accept(ctx context.Context, conn io.ReadWriteCloser, lPrivKey ed25519.PrivateKey) (ed25519.PublicKey, []byte, error) {
|
||||||
logger := logging.FromContext(ctx).WithGroup("handshake")
|
logger := logging.FromContext(ctx).WithGroup("handshake")
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -281,9 +287,10 @@ func Accept(ctx context.Context, conn io.ReadWriteCloser, lPrivKey *ecdsa.Privat
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
log.Println(string(plaintext), len(plaintext))
|
||||||
|
|
||||||
clientPublicKeyRaw := plaintext[:identity.ExportedPublicKeySize]
|
clientPublicKeyRaw := plaintext[:44]
|
||||||
signature := plaintext[identity.ExportedPublicKeySize:]
|
signature := plaintext[44:]
|
||||||
|
|
||||||
// Verify the client's public key and signature.
|
// Verify the client's public key and signature.
|
||||||
logger.DebugContext(ctx, "importing client's public signing key")
|
logger.DebugContext(ctx, "importing client's public signing key")
|
||||||
|
@ -3,7 +3,7 @@ package handshake_test
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ed25519"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
@ -17,9 +17,9 @@ import (
|
|||||||
func TestHandshake(t *testing.T) {
|
func TestHandshake(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// alice, represented by an ecdsa key pair.
|
// alice, represented by an ed25519 key pair.
|
||||||
alice, _ := identity.Generate()
|
alice, _ := identity.Generate()
|
||||||
// bob, also represented by an ecdsa key pair.
|
// bob, also represented by an ed25519 key pair.
|
||||||
bob, _ := identity.Generate()
|
bob, _ := identity.Generate()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -28,7 +28,7 @@ func TestHandshake(t *testing.T) {
|
|||||||
// any errors produce by alice
|
// any errors produce by alice
|
||||||
aliceErr error
|
aliceErr error
|
||||||
// alice's public key as discovered by bob
|
// alice's public key as discovered by bob
|
||||||
discoveredAlicePublicKey *ecdsa.PublicKey
|
discoveredAlicePublicKey ed25519.PublicKey
|
||||||
|
|
||||||
// the secret that bob should arrive at on his own
|
// the secret that bob should arrive at on his own
|
||||||
bobSecret []byte
|
bobSecret []byte
|
||||||
@ -47,7 +47,7 @@ func TestHandshake(t *testing.T) {
|
|||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
go func() {
|
go func() {
|
||||||
aliceSecret, aliceErr = handshake.Initiate(ctx, client, alice, &bob.PublicKey)
|
aliceSecret, aliceErr = handshake.Initiate(ctx, client, alice, identity.ToPublicKey(bob))
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
@ -76,7 +76,7 @@ func TestHandshake(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Bob should have discovered alice's public key.
|
// Bob should have discovered alice's public key.
|
||||||
if !alice.PublicKey.Equal(discoveredAlicePublicKey) {
|
if !identity.ToPublicKey(alice).Equal(discoveredAlicePublicKey) {
|
||||||
t.Errorf("handshake failed: discovered public key is not equal to alice's public key")
|
t.Errorf("handshake failed: discovered public key is not equal to alice's public key")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -93,9 +93,9 @@ func BenchmarkHandshake(b *testing.B) {
|
|||||||
func runSimulation() error {
|
func runSimulation() error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// alice, represented by an ecdsa key pair.
|
// alice, represented by an ed25519 key pair.
|
||||||
alice, _ := identity.Generate()
|
alice, _ := identity.Generate()
|
||||||
// bob, also represented by an ecdsa key pair.
|
// bob, also represented by an ed25519 key pair.
|
||||||
bob, _ := identity.Generate()
|
bob, _ := identity.Generate()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -123,7 +123,7 @@ func runSimulation() error {
|
|||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
go func() {
|
go func() {
|
||||||
_, aliceErr = handshake.Initiate(ctx, client, alice, &bob.PublicKey)
|
_, aliceErr = handshake.Initiate(ctx, client, alice, identity.ToPublicKey(bob))
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ed25519"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -22,11 +22,11 @@ type Addr struct {
|
|||||||
network string
|
network string
|
||||||
ip net.IP
|
ip net.IP
|
||||||
port int
|
port int
|
||||||
publicKey *ecdsa.PublicKey
|
publicKey ed25519.PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAddr creates a new Addr.
|
// NewAddr creates a new Addr.
|
||||||
func NewAddr(ip net.IP, port int, publicKey *ecdsa.PublicKey) *Addr {
|
func NewAddr(ip net.IP, port int, publicKey ed25519.PublicKey) *Addr {
|
||||||
network := "dsfx"
|
network := "dsfx"
|
||||||
return &Addr{network, ip, port, publicKey}
|
return &Addr{network, ip, port, publicKey}
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ func ParseAddr(addrRaw string) (*Addr, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FromTCPAddr creates a new Addr from a net.TCPAddr.
|
// FromTCPAddr creates a new Addr from a net.TCPAddr.
|
||||||
func FromTCPAddr(addr *net.TCPAddr, publicKey *ecdsa.PublicKey) *Addr {
|
func FromTCPAddr(addr *net.TCPAddr, publicKey ed25519.PublicKey) *Addr {
|
||||||
return &Addr{
|
return &Addr{
|
||||||
network: "dsfx",
|
network: "dsfx",
|
||||||
ip: addr.IP,
|
ip: addr.IP,
|
||||||
@ -102,7 +102,7 @@ func (a *Addr) Port() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PublicKey returns the public key of the Addr.
|
// PublicKey returns the public key of the Addr.
|
||||||
func (a *Addr) PublicKey() *ecdsa.PublicKey {
|
func (a *Addr) PublicKey() ed25519.PublicKey {
|
||||||
return a.publicKey
|
return a.publicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ed25519"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -14,12 +14,12 @@ import (
|
|||||||
type Conn struct {
|
type Conn struct {
|
||||||
conn *net.TCPConn
|
conn *net.TCPConn
|
||||||
sessionKey []byte
|
sessionKey []byte
|
||||||
localIdentity *ecdsa.PublicKey
|
localIdentity ed25519.PublicKey
|
||||||
remoteIdentity *ecdsa.PublicKey
|
remoteIdentity ed25519.PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConn creates a new Conn.
|
// NewConn creates a new Conn.
|
||||||
func NewConn(conn *net.TCPConn, sessionKey []byte, localIdentity, remoteIdentity *ecdsa.PublicKey) *Conn {
|
func NewConn(conn *net.TCPConn, sessionKey []byte, localIdentity, remoteIdentity ed25519.PublicKey) *Conn {
|
||||||
return &Conn{conn, sessionKey, localIdentity, remoteIdentity}
|
return &Conn{conn, sessionKey, localIdentity, remoteIdentity}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,11 @@ package network
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ed25519"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"koti.casa/numenor-labs/dsfx/pkg/crypto/identity"
|
||||||
"koti.casa/numenor-labs/dsfx/pkg/handshake"
|
"koti.casa/numenor-labs/dsfx/pkg/handshake"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ import (
|
|||||||
type Listener struct {
|
type Listener struct {
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
tcpListener *net.TCPListener
|
tcpListener *net.TCPListener
|
||||||
identity *ecdsa.PrivateKey
|
identity ed25519.PrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accept implements net.Listener.
|
// Accept implements net.Listener.
|
||||||
@ -30,7 +31,7 @@ func (l *Listener) Accept() (net.Conn, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewConn(conn, sessionKey, &l.identity.PublicKey, clientIdentity), nil
|
return NewConn(conn, sessionKey, identity.ToPublicKey(l.identity), clientIdentity), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements net.Listener.
|
// Close implements net.Listener.
|
||||||
@ -42,5 +43,5 @@ func (l *Listener) Close() error {
|
|||||||
func (l *Listener) Addr() net.Addr {
|
func (l *Listener) Addr() net.Addr {
|
||||||
laddr := l.tcpListener.Addr().(*net.TCPAddr)
|
laddr := l.tcpListener.Addr().(*net.TCPAddr)
|
||||||
|
|
||||||
return NewAddr(laddr.IP, laddr.Port, &l.identity.PublicKey)
|
return NewAddr(laddr.IP, laddr.Port, identity.ToPublicKey(l.identity))
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package network
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ed25519"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"koti.casa/numenor-labs/dsfx/pkg/handshake"
|
"koti.casa/numenor-labs/dsfx/pkg/handshake"
|
||||||
@ -12,7 +12,7 @@ import (
|
|||||||
// Dial ...
|
// Dial ...
|
||||||
func Dial(
|
func Dial(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
identity *ecdsa.PrivateKey,
|
identity ed25519.PrivateKey,
|
||||||
laddr *Addr,
|
laddr *Addr,
|
||||||
raddr *Addr,
|
raddr *Addr,
|
||||||
) (*Conn, error) {
|
) (*Conn, error) {
|
||||||
@ -32,7 +32,7 @@ func Dial(
|
|||||||
// Listen ...
|
// Listen ...
|
||||||
func Listen(
|
func Listen(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
identity *ecdsa.PrivateKey,
|
identity ed25519.PrivateKey,
|
||||||
laddr *Addr,
|
laddr *Addr,
|
||||||
) (net.Listener, error) {
|
) (net.Listener, error) {
|
||||||
tcpListener, err := net.ListenTCP("tcp", laddr.TCPAddr())
|
tcpListener, err := net.ListenTCP("tcp", laddr.TCPAddr())
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@ -15,17 +13,10 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode the private key to der
|
exported, err := identity.ExportPrivateKey(key)
|
||||||
der, err := x509.MarshalECPrivateKey(key)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode the private key to PEM
|
fmt.Fprint(os.Stdout, string(exported))
|
||||||
pem := pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "PRIVATE KEY",
|
|
||||||
Bytes: der,
|
|
||||||
})
|
|
||||||
|
|
||||||
fmt.Fprint(os.Stdout, string(pem))
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user