diff --git a/README.md b/README.md index fb0c409..e05edad 100644 --- a/README.md +++ b/README.md @@ -33,13 +33,13 @@ cd dsfx Build the project: ```sh -go build -o dist/ ./... +go build -o dist/ ./cmd/... ``` You can also install the executables to your $GOPATH/bin: ```sh -go install ./... +go install ./cmd/... ``` --- @@ -48,13 +48,13 @@ go install ./... ### Starting the Server -The dsfx-server 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 (ECDSA private key in PEM format). For example: ```sh -dsfx-server -host localhost -port 8000 -key /path/to/serverkey.pem +dsfxnode -host localhost -port 8000 -key /path/to/serverkey.pem ``` -> Note, if you need to generate a new ECDSA key, you can use the following command: `go run ./cmd/genkey > path/to/masterkey.pem` +> Note, if you need to generate a new ECDSA key, you can use the following command: `go run ./tool/genkey > path/to/masterkey.pem` Command-line flags for dsfx-server: @@ -69,14 +69,14 @@ File path to the PEM-encoded ECDSA private key that serves as the server’s mas 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 Client +### Running the Admin Client -The dsfx-client 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 ECDSA key in PEM format) and currently supports only the “test” command for checking connectivity to the server. Client command usage: ```sh -/dsfx-client -key /path/to/clientkey.pem test +dsfxctl -key /path/to/clientkey.pem test ``` Where: @@ -96,7 +96,7 @@ For example, `dsfx://127.0.0.1:8000#” or “dsfx://127.0.0.1:80 Example: ```sh -dsfx-client -key ./dsfx-client/masterkey test dsfx://127.0.0.1:8000#eyJuIjoiLy8v.. +dsfxctl -key ./dsfx-client/masterkey test dsfx://127.0.0.1:8000#eyJuIjoiLy8v.. ``` If no command or an unrecognized command is provided, the client will print a brief usage message and exit. @@ -106,8 +106,8 @@ If no command or an unrecognized command is provided, the client will print a br For quick help, simply pass the -h flag: ```sh -dsfx-server -h -dsfx-client -h +dsfxctl -h +dsfxnode -h ``` This will display the usage information along with available flags. diff --git a/cmd/dsfx/main.go b/cmd/dsfx/main.go new file mode 100644 index 0000000..047c9fb --- /dev/null +++ b/cmd/dsfx/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "log" +) + +func main() { + log.Println("hello from dsfx!") +} diff --git a/dsfx-client/main.go b/cmd/dsfxctl/main.go similarity index 80% rename from dsfx-client/main.go rename to cmd/dsfxctl/main.go index 5e8c0e8..7bff2a6 100644 --- a/dsfx-client/main.go +++ b/cmd/dsfxctl/main.go @@ -9,9 +9,9 @@ import ( "net" "os" - "koti.casa/numenor-labs/dsfx/shared/dcrypto" - "koti.casa/numenor-labs/dsfx/shared/dlog" - "koti.casa/numenor-labs/dsfx/shared/dnet" + "koti.casa/numenor-labs/dsfx/pkg/crypto/identity" + "koti.casa/numenor-labs/dsfx/pkg/logging" + "koti.casa/numenor-labs/dsfx/pkg/network" ) func main() { @@ -31,13 +31,13 @@ func main() { // where the context is not available, or the context is not a child of the // context with the logger, the default logger will be used. slog.SetDefault(logger) - ctx = dlog.WithContext(ctx, logger) + ctx = logging.WithContext(ctx, logger) // --------------------------------------------------------------------------- // Commands flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage: %s [command] [args]\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "Usage: dsfxctl [command] [args]\n") fmt.Fprintf(os.Stderr, "Commands:\n") fmt.Fprintf(os.Stderr, " test Test the connection to the server\n") fmt.Fprintf(os.Stderr, "Flags:\n") @@ -53,13 +53,13 @@ func main() { os.Exit(1) } - identity, err := dcrypto.LoadSigningKeyFromFile(*flagKey) + identity, err := identity.LoadSigningKeyFromFile(*flagKey) if err != nil { logger.ErrorContext(ctx, "failed to load private key", slog.Any("error", err)) os.Exit(1) } - laddr := dnet.NewAddr( + laddr := network.NewAddr( net.ParseIP("0.0.0.0"), 0, // port 0 means any available port &identity.PublicKey, @@ -84,16 +84,16 @@ func main() { } } -func testConnection(ctx context.Context, identity *ecdsa.PrivateKey, laddr *dnet.Addr, raddrRaw string) { - logger := dlog.FromContext(context.Background()) +func testConnection(ctx context.Context, identity *ecdsa.PrivateKey, laddr *network.Addr, raddrRaw string) { + logger := logging.FromContext(context.Background()) - raddr, err := dnet.ParseAddr(raddrRaw) + raddr, err := network.ParseAddr(raddrRaw) if err != nil { logger.ErrorContext(ctx, "failed to parse server address", slog.Any("error", err)) return } - conn, err := dnet.Dial(ctx, identity, laddr, raddr) + conn, err := network.Dial(ctx, identity, laddr, raddr) if err != nil { logger.ErrorContext(ctx, "failed to connect", slog.Any("error", err)) return diff --git a/dsfx-server/main.go b/cmd/dsfxnode/main.go similarity index 85% rename from dsfx-server/main.go rename to cmd/dsfxnode/main.go index ecbcb37..8f7e97e 100644 --- a/dsfx-server/main.go +++ b/cmd/dsfxnode/main.go @@ -8,9 +8,9 @@ import ( "net" "os" - "koti.casa/numenor-labs/dsfx/shared/dcrypto" - "koti.casa/numenor-labs/dsfx/shared/dlog" - "koti.casa/numenor-labs/dsfx/shared/dnet" + "koti.casa/numenor-labs/dsfx/pkg/crypto/identity" + "koti.casa/numenor-labs/dsfx/pkg/logging" + "koti.casa/numenor-labs/dsfx/pkg/network" ) var ( @@ -36,7 +36,7 @@ func main() { // where the context is not available, or the context is not a child of the // context with the logger, the default logger will be used. slog.SetDefault(logger) - ctx = dlog.WithContext(ctx, logger) + ctx = logging.WithContext(ctx, logger) // --------------------------------------------------------------------------- // Flags @@ -48,7 +48,7 @@ func main() { os.Exit(1) } - masterKey, err := dcrypto.LoadSigningKeyFromFile(*flagKey) + masterKey, err := identity.LoadSigningKeyFromFile(*flagKey) if err != nil { logger.ErrorContext(ctx, "failed to load master key", slog.Any("error", err)) os.Exit(1) @@ -60,12 +60,12 @@ func main() { slog.ErrorContext(ctx, "invalid host or port") os.Exit(1) } - addr := dnet.FromTCPAddr(tcpAddr, &masterKey.PublicKey) + addr := network.FromTCPAddr(tcpAddr, &masterKey.PublicKey) // --------------------------------------------------------------------------- // Listener - listener, err := dnet.Listen(ctx, masterKey, addr) + listener, err := network.Listen(ctx, masterKey, addr) if err != nil { logger.ErrorContext(ctx, "listener error", slog.Any("error", err)) os.Exit(1) @@ -87,7 +87,7 @@ func main() { func handleConnection(ctx context.Context, conn net.Conn) error { defer conn.Close() - logger := dlog.FromContext(ctx) + logger := logging.FromContext(ctx) msg := make([]byte, 1024) n, err := conn.Read(msg) diff --git a/shared/dcrypto/aead.go b/pkg/crypto/encryption/aead.go similarity index 98% rename from shared/dcrypto/aead.go rename to pkg/crypto/encryption/aead.go index ed799d0..03ab39e 100644 --- a/shared/dcrypto/aead.go +++ b/pkg/crypto/encryption/aead.go @@ -1,4 +1,4 @@ -package dcrypto +package encryption import ( "crypto/aes" diff --git a/shared/dcrypto/ecdsa.go b/pkg/crypto/identity/ecdsa.go similarity index 99% rename from shared/dcrypto/ecdsa.go rename to pkg/crypto/identity/ecdsa.go index c5d5026..5005af5 100644 --- a/shared/dcrypto/ecdsa.go +++ b/pkg/crypto/identity/ecdsa.go @@ -1,4 +1,4 @@ -package dcrypto +package identity import ( "crypto/ecdsa" diff --git a/shared/dcrypto/ecdh.go b/pkg/crypto/keyexchange/ecdh.go similarity index 77% rename from shared/dcrypto/ecdh.go rename to pkg/crypto/keyexchange/ecdh.go index 4286405..34ac89e 100644 --- a/shared/dcrypto/ecdh.go +++ b/pkg/crypto/keyexchange/ecdh.go @@ -1,8 +1,10 @@ -package dcrypto +package keyexchange import ( "crypto/ecdh" + "crypto/hkdf" "crypto/rand" + "crypto/sha256" ) var ( @@ -17,7 +19,17 @@ func GenerateDHKey() (*ecdh.PrivateKey, error) { // ComputeDHSecret computes the shared secret from the private key and the public key. func ComputeDHSecret(priv *ecdh.PrivateKey, pub *ecdh.PublicKey) ([]byte, error) { - return priv.ECDH(pub) + secret, err := priv.ECDH(pub) + if err != nil { + return nil, err + } + + key, err := hkdf.Key(sha256.New, secret, nil, "", 32) + if err != nil { + return nil, err + } + + return key, nil } // ExportDHPublicKey exports the public key as a byte slice. diff --git a/shared/dnet/frame.go b/pkg/frame/frame.go similarity index 99% rename from shared/dnet/frame.go rename to pkg/frame/frame.go index fe6e2e0..9cfa628 100644 --- a/shared/dnet/frame.go +++ b/pkg/frame/frame.go @@ -1,4 +1,4 @@ -package dnet +package frame import ( "encoding/binary" diff --git a/shared/dnet/frame_test.go b/pkg/frame/frame_test.go similarity index 90% rename from shared/dnet/frame_test.go rename to pkg/frame/frame_test.go index 109368f..593a801 100644 --- a/shared/dnet/frame_test.go +++ b/pkg/frame/frame_test.go @@ -1,11 +1,11 @@ -package dnet_test +package frame_test import ( "bufio" "bytes" "testing" - "koti.casa/numenor-labs/dsfx/shared/dnet" + "koti.casa/numenor-labs/dsfx/pkg/frame" ) func TestLenPrefixedWriteTo(t *testing.T) { @@ -16,7 +16,7 @@ func TestLenPrefixedWriteTo(t *testing.T) { // When ... - n, err := dnet.NewFrame(msg).WriteTo(buf) + n, err := frame.NewFrame(msg).WriteTo(buf) // Then ... @@ -49,7 +49,7 @@ func TestLenPrefixedReadFrom(t *testing.T) { expectedBytesRead := len(msg) // When ... - f := dnet.NewFrame(nil) + f := frame.NewFrame(nil) n, err := f.ReadFrom(buf) // Then ... diff --git a/shared/dnet/bench.txt b/pkg/handshake/bench.txt similarity index 100% rename from shared/dnet/bench.txt rename to pkg/handshake/bench.txt diff --git a/shared/dnet/handshake.go b/pkg/handshake/handshake.go similarity index 79% rename from shared/dnet/handshake.go rename to pkg/handshake/handshake.go index f2c2843..3d3697a 100644 --- a/shared/dnet/handshake.go +++ b/pkg/handshake/handshake.go @@ -1,4 +1,4 @@ -package dnet +package handshake import ( "bytes" @@ -11,8 +11,11 @@ import ( "log/slog" "koti.casa/numenor-labs/dsfx/pkg/assert" - "koti.casa/numenor-labs/dsfx/shared/dcrypto" - "koti.casa/numenor-labs/dsfx/shared/dlog" + "koti.casa/numenor-labs/dsfx/pkg/crypto/encryption" + "koti.casa/numenor-labs/dsfx/pkg/crypto/identity" + "koti.casa/numenor-labs/dsfx/pkg/crypto/keyexchange" + "koti.casa/numenor-labs/dsfx/pkg/frame" + "koti.casa/numenor-labs/dsfx/pkg/logging" ) const ( @@ -34,14 +37,14 @@ func Handshake( lPrivKey *ecdsa.PrivateKey, rPubKey *ecdsa.PublicKey, ) ([]byte, error) { - logger := dlog.FromContext(ctx).WithGroup("handshake") + logger := logging.FromContext(ctx).WithGroup("handshake") // ------------------------------------------------------------------------ // Step 1: Ephemeral Key Exchange To Server logger.DebugContext(ctx, "creating dh key") // Create a new ECDH private key for the actor. - ourDHKey, err := dcrypto.GenerateDHKey() + ourDHKey, err := keyexchange.GenerateDHKey() if err != nil { return nil, err } @@ -49,7 +52,7 @@ func Handshake( logger.DebugContext(ctx, "exporting dh key") // Export the public key of the actor's ECDH private key. - ourDHKeyRaw, err := dcrypto.ExportDHPublicKey(ourDHKey.PublicKey()) + ourDHKeyRaw, err := keyexchange.ExportDHPublicKey(ourDHKey.PublicKey()) if err != nil { return nil, err } @@ -57,7 +60,7 @@ func Handshake( // Write the actor's public key to the connection. logger.DebugContext(ctx, "sending dh key", slog.Int("key.size", len(ourDHKeyRaw))) - _, err = NewFrame(ourDHKeyRaw).WriteTo(conn) + _, err = frame.NewFrame(ourDHKeyRaw).WriteTo(conn) if err != nil { return nil, err } @@ -67,7 +70,7 @@ func Handshake( // Read the remote actor's public key from the connection. logger.DebugContext(ctx, "waiting for server's dh key") - remoteDHKeyFrame := NewFrame(nil) + remoteDHKeyFrame := frame.NewFrame(nil) _, err = remoteDHKeyFrame.ReadFrom(conn) if err != nil { return nil, err @@ -78,7 +81,7 @@ func Handshake( // Import the remote actor's public key. logger.DebugContext(ctx, "importing server's dh key") - remoteDHKey, err := dcrypto.ImportDHPublicKey(remoteDHKeyFrame.Contents()) + remoteDHKey, err := keyexchange.ImportDHPublicKey(remoteDHKeyFrame.Contents()) if err != nil { return nil, err } @@ -88,13 +91,13 @@ func Handshake( // Export the public key of the actor's signing key. logger.DebugContext(ctx, "exporting public signing key") - ourPublicKeyRaw, err := dcrypto.ExportPublicSigningKey(&lPrivKey.PublicKey) + ourPublicKeyRaw, err := identity.ExportPublicSigningKey(&lPrivKey.PublicKey) if err != nil { return nil, err } logger.DebugContext(ctx, "exporting remote public signing key") - remotePublicKeyRaw, err := dcrypto.ExportPublicSigningKey(rPubKey) + remotePublicKeyRaw, err := identity.ExportPublicSigningKey(rPubKey) if err != nil { return nil, err } @@ -114,26 +117,18 @@ func Handshake( // Sign the message with the actor's private key. logger.DebugContext(ctx, "signing authentication message") - signature, err := dcrypto.Sign(lPrivKey, authMessage) + signature, err := identity.Sign(lPrivKey, authMessage) if err != nil { return nil, err } // Compute the shared secret between the actor and the remote actor. logger.DebugContext(ctx, "computing shared secret") - sharedSecret, err := dcrypto.ComputeDHSecret(ourDHKey, remoteDHKey) + derivedKey, err := keyexchange.ComputeDHSecret(ourDHKey, remoteDHKey) if err != nil { return nil, err } - assert.Assert(len(sharedSecret) == 48, "invalid shared secret size") - - // Derive a key from the shared secret using HKDF. - logger.DebugContext(ctx, "deriving key from shared secret") - derivedKey, err := dcrypto.Key(sharedSecret, nil) - if err != nil { - return nil, err - } - assert.Assert(len(derivedKey) == 32, "invalid derived key size") + assert.Assert(len(derivedKey) == 32, "invalid shared secret size") plaintext := make([]byte, 0, len(ourPublicKeyRaw)+len(signature)) plaintext = append(plaintext, ourPublicKeyRaw...) @@ -141,14 +136,14 @@ func Handshake( // Encrypt the message with the derived key. logger.DebugContext(ctx, "encrypting authentication message") - boxedMsg, err := dcrypto.Encrypt(derivedKey, plaintext) + boxedMsg, err := encryption.Encrypt(derivedKey, plaintext) if err != nil { return nil, err } // Write the boxed message to the connection. logger.DebugContext(ctx, "sending authentication message", slog.Int("message.size", len(boxedMsg))) - _, err = NewFrame(boxedMsg).WriteTo(conn) + _, err = frame.NewFrame(boxedMsg).WriteTo(conn) if err != nil { return nil, err } @@ -158,7 +153,7 @@ func Handshake( // Read the authentication message from the connection. logger.DebugContext(ctx, "waiting for server's authentication message") - authMessageFrame := NewFrame(nil) + authMessageFrame := frame.NewFrame(nil) n, err := authMessageFrame.ReadFrom(conn) if err != nil { return nil, err @@ -167,7 +162,7 @@ func Handshake( // Decrypt the authentication message with the derived key. logger.DebugContext(ctx, "decrypting authentication message") - plaintext, err = dcrypto.Decrypt(derivedKey, authMessageFrame.Contents()) + plaintext, err = encryption.Decrypt(derivedKey, authMessageFrame.Contents()) if err != nil { return nil, err } @@ -175,20 +170,20 @@ func Handshake( // The server authentication is just verifying the signature it created of // the client authentication message. logger.DebugContext(ctx, "importing server's public signing key") - remotePublicKey, err := dcrypto.ImportPublicSigningKey(remotePublicKeyRaw) + remotePublicKey, err := identity.ImportPublicSigningKey(remotePublicKeyRaw) if err != nil { return nil, err } logger.DebugContext(ctx, "verifying server's signature") - if !dcrypto.Verify(remotePublicKey, authMessage, plaintext) { + if !identity.Verify(remotePublicKey, authMessage, plaintext) { return nil, errors.New("failed to verify server's signature") } // Finally, we need to let the server know that the handshake is complete. logger.DebugContext(ctx, "sending handshake complete message") handshakeCompleteMsg := []byte{0x01} - _, err = NewFrame(handshakeCompleteMsg).WriteTo(conn) + _, err = frame.NewFrame(handshakeCompleteMsg).WriteTo(conn) if err != nil { return nil, err } @@ -200,14 +195,14 @@ func Handshake( // AcceptHandshake accepts a handshake from the given actor and connection. It // returns the shared secret between the actor and the remote actor. func AcceptHandshake(ctx context.Context, conn io.ReadWriteCloser, lPrivKey *ecdsa.PrivateKey) (*ecdsa.PublicKey, []byte, error) { - logger := dlog.FromContext(ctx).WithGroup("handshake") + logger := logging.FromContext(ctx).WithGroup("handshake") // ------------------------------------------------------------------------ // Step 1: Ephemeral Key Exchange From Client // Read the remote actor's public key from the connection. logger.DebugContext(ctx, "waiting for client's dh key") - remoteDHKeyFrame := NewFrame(nil) + remoteDHKeyFrame := frame.NewFrame(nil) _, err := remoteDHKeyFrame.ReadFrom(conn) if err != nil { return nil, nil, err @@ -215,7 +210,7 @@ func AcceptHandshake(ctx context.Context, conn io.ReadWriteCloser, lPrivKey *ecd // Import the remote actor's public key. logger.DebugContext(ctx, "importing client's dh key") - remoteDHKey, err := dcrypto.ImportDHPublicKey(remoteDHKeyFrame.Contents()) + remoteDHKey, err := keyexchange.ImportDHPublicKey(remoteDHKeyFrame.Contents()) if err != nil { return nil, nil, err } @@ -225,21 +220,21 @@ func AcceptHandshake(ctx context.Context, conn io.ReadWriteCloser, lPrivKey *ecd // Create a new ECDH private key for the actor. logger.DebugContext(ctx, "creating dh key") - ourDHKey, err := dcrypto.GenerateDHKey() + ourDHKey, err := keyexchange.GenerateDHKey() if err != nil { return nil, nil, err } // Export the public key of the actor's ECDH private key. logger.DebugContext(ctx, "exporting dh key") - ourDHKeyRaw, err := dcrypto.ExportDHPublicKey(ourDHKey.PublicKey()) + ourDHKeyRaw, err := keyexchange.ExportDHPublicKey(ourDHKey.PublicKey()) if err != nil { return nil, nil, err } // Write the actor's public key to the connection. logger.DebugContext(ctx, "sending dh key", slog.Int("key.size", len(ourDHKeyRaw))) - _, err = NewFrame(ourDHKeyRaw).WriteTo(conn) + _, err = frame.NewFrame(ourDHKeyRaw).WriteTo(conn) if err != nil { return nil, nil, err } @@ -249,7 +244,7 @@ func AcceptHandshake(ctx context.Context, conn io.ReadWriteCloser, lPrivKey *ecd // Read the authentication message from the connection. logger.DebugContext(ctx, "waiting for client's authentication message") - authMessageFrame := NewFrame(nil) + authMessageFrame := frame.NewFrame(nil) n, err := authMessageFrame.ReadFrom(conn) if err != nil { return nil, nil, err @@ -258,20 +253,13 @@ func AcceptHandshake(ctx context.Context, conn io.ReadWriteCloser, lPrivKey *ecd // Decrypt the authentication message with the derived key. logger.DebugContext(ctx, "computing shared secret") - sharedSecret, err := dcrypto.ComputeDHSecret(ourDHKey, remoteDHKey) - if err != nil { - return nil, nil, err - } - - // Derive a key from the shared secret using HKDF. - logger.DebugContext(ctx, "deriving key from shared secret") - derivedKey, err := dcrypto.Key(sharedSecret, nil) + derivedKey, err := keyexchange.ComputeDHSecret(ourDHKey, remoteDHKey) if err != nil { return nil, nil, err } logger.DebugContext(ctx, "decrypting authentication message") - plaintext, err := dcrypto.Decrypt(derivedKey, authMessageFrame.Contents()) + plaintext, err := encryption.Decrypt(derivedKey, authMessageFrame.Contents()) if err != nil { return nil, nil, err } @@ -281,7 +269,7 @@ func AcceptHandshake(ctx context.Context, conn io.ReadWriteCloser, lPrivKey *ecd // Verify the client's public key and signature. logger.DebugContext(ctx, "importing client's public signing key") - clientPublicKey, err := dcrypto.ImportPublicSigningKey(clientPublicKeyRaw) + clientPublicKey, err := identity.ImportPublicSigningKey(clientPublicKeyRaw) if err != nil { return nil, nil, err } @@ -300,7 +288,7 @@ func AcceptHandshake(ctx context.Context, conn io.ReadWriteCloser, lPrivKey *ecd } logger.DebugContext(ctx, "verifying client's signature") - if !dcrypto.Verify(clientPublicKey, authMessage, signature) { + if !identity.Verify(clientPublicKey, authMessage, signature) { return nil, nil, errors.New("failed to verify client's signature") } @@ -308,27 +296,27 @@ func AcceptHandshake(ctx context.Context, conn io.ReadWriteCloser, lPrivKey *ecd // key. This will be sent back to the client in the next step to authenticate // the server to the client. logger.DebugContext(ctx, "signing authentication message") - serverSignature, err := dcrypto.Sign(lPrivKey, authMessage) + serverSignature, err := identity.Sign(lPrivKey, authMessage) if err != nil { return nil, nil, err } logger.DebugContext(ctx, "encrypting server's signature") - boxedMsg, err := dcrypto.Encrypt(derivedKey, serverSignature) + boxedMsg, err := encryption.Encrypt(derivedKey, serverSignature) if err != nil { return nil, nil, err } // Send the server's signature back to the client. logger.DebugContext(ctx, "sending authentication message", slog.Int("message.size", len(boxedMsg))) - _, err = NewFrame(boxedMsg).WriteTo(conn) + _, err = frame.NewFrame(boxedMsg).WriteTo(conn) if err != nil { return nil, nil, err } logger.DebugContext(ctx, "waiting for handshake complete message") // Read the handshake complete message from the client. - handshakeCompleteFrame := NewFrame(nil) + handshakeCompleteFrame := frame.NewFrame(nil) _, err = handshakeCompleteFrame.ReadFrom(conn) if err != nil { return nil, nil, err @@ -345,11 +333,11 @@ func AcceptHandshake(ctx context.Context, conn io.ReadWriteCloser, lPrivKey *ecd } func buildMessage(clientPubKey *ecdh.PublicKey, serverPubKey *ecdh.PublicKey) ([]byte, error) { - clientPubKeyRaw, err := dcrypto.ExportDHPublicKey(clientPubKey) + clientPubKeyRaw, err := keyexchange.ExportDHPublicKey(clientPubKey) if err != nil { return nil, err } - serverPubKeyRaw, err := dcrypto.ExportDHPublicKey(serverPubKey) + serverPubKeyRaw, err := keyexchange.ExportDHPublicKey(serverPubKey) if err != nil { return nil, err } diff --git a/shared/dnet/handshake_test.go b/pkg/handshake/handshake_test.go similarity index 83% rename from shared/dnet/handshake_test.go rename to pkg/handshake/handshake_test.go index bc33b79..ac8a633 100644 --- a/shared/dnet/handshake_test.go +++ b/pkg/handshake/handshake_test.go @@ -1,4 +1,4 @@ -package dnet_test +package handshake_test import ( "bytes" @@ -10,17 +10,17 @@ import ( "sync" "testing" - "koti.casa/numenor-labs/dsfx/shared/dcrypto" - "koti.casa/numenor-labs/dsfx/shared/dnet" + "koti.casa/numenor-labs/dsfx/pkg/crypto/identity" + "koti.casa/numenor-labs/dsfx/pkg/handshake" ) func TestHandshake(t *testing.T) { ctx := context.Background() // alice, represented by an ecdsa key pair. - alice, _ := dcrypto.GenerateSigningKey() + alice, _ := identity.GenerateSigningKey() // bob, also represented by an ecdsa key pair. - bob, _ := dcrypto.GenerateSigningKey() + bob, _ := identity.GenerateSigningKey() var ( // the secret that alice should arrive at on her own @@ -47,11 +47,11 @@ func TestHandshake(t *testing.T) { var wg sync.WaitGroup wg.Add(2) go func() { - aliceSecret, aliceErr = dnet.Handshake(ctx, client, alice, &bob.PublicKey) + aliceSecret, aliceErr = handshake.Handshake(ctx, client, alice, &bob.PublicKey) wg.Done() }() go func() { - discoveredAlicePublicKey, bobSecret, bobErr = dnet.AcceptHandshake(ctx, server, bob) + discoveredAlicePublicKey, bobSecret, bobErr = handshake.AcceptHandshake(ctx, server, bob) wg.Done() }() wg.Wait() @@ -94,9 +94,9 @@ func runSimulation() error { ctx := context.Background() // alice, represented by an ecdsa key pair. - alice, _ := dcrypto.GenerateSigningKey() + alice, _ := identity.GenerateSigningKey() // bob, also represented by an ecdsa key pair. - bob, _ := dcrypto.GenerateSigningKey() + bob, _ := identity.GenerateSigningKey() var ( // the secret that alice should arrive at on her own @@ -123,11 +123,11 @@ func runSimulation() error { var wg sync.WaitGroup wg.Add(2) go func() { - _, aliceErr = dnet.Handshake(ctx, client, alice, &bob.PublicKey) + _, aliceErr = handshake.Handshake(ctx, client, alice, &bob.PublicKey) wg.Done() }() go func() { - _, _, bobErr = dnet.AcceptHandshake(ctx, server, bob) + _, _, bobErr = handshake.AcceptHandshake(ctx, server, bob) wg.Done() }() wg.Wait() diff --git a/shared/dlog/dlog.go b/pkg/logging/logging.go similarity index 96% rename from shared/dlog/dlog.go rename to pkg/logging/logging.go index 6fff932..69a5b36 100644 --- a/shared/dlog/dlog.go +++ b/pkg/logging/logging.go @@ -1,4 +1,4 @@ -package dlog +package logging import ( "context" diff --git a/shared/dnet/addr.go b/pkg/network/addr.go similarity index 92% rename from shared/dnet/addr.go rename to pkg/network/addr.go index 2abb561..39fc799 100644 --- a/shared/dnet/addr.go +++ b/pkg/network/addr.go @@ -1,4 +1,4 @@ -package dnet +package network import ( "crypto/ecdsa" @@ -8,7 +8,7 @@ import ( "net" "strings" - "koti.casa/numenor-labs/dsfx/shared/dcrypto" + "koti.casa/numenor-labs/dsfx/pkg/crypto/identity" ) var ( @@ -60,7 +60,7 @@ func ParseAddr(addrRaw string) (*Addr, error) { return nil, ErrInvalidFormat } - publicKey, err := dcrypto.ImportPublicSigningKey(publicKeyBytes) + publicKey, err := identity.ImportPublicSigningKey(publicKeyBytes) if err != nil { return nil, ErrInvalidFormat } @@ -86,7 +86,7 @@ func (a *Addr) Network() string { // String implements net.Addr. func (a *Addr) String() string { - exported, _ := dcrypto.ExportPublicSigningKey(a.publicKey) + exported, _ := identity.ExportPublicSigningKey(a.publicKey) exportedBase64 := base64.StdEncoding.EncodeToString(exported) return fmt.Sprintf("%s://%s:%d#%s", a.network, a.ip, a.port, exportedBase64) } diff --git a/shared/dnet/conn.go b/pkg/network/conn.go similarity index 87% rename from shared/dnet/conn.go rename to pkg/network/conn.go index 1f1089c..083c441 100644 --- a/shared/dnet/conn.go +++ b/pkg/network/conn.go @@ -1,11 +1,12 @@ -package dnet +package network import ( "crypto/ecdsa" "net" "time" - "koti.casa/numenor-labs/dsfx/shared/dcrypto" + "koti.casa/numenor-labs/dsfx/pkg/crypto/encryption" + "koti.casa/numenor-labs/dsfx/pkg/frame" ) // Conn is a wrapper around net.TCPConn that encrypts and decrypts data as it is @@ -27,12 +28,12 @@ func NewConn(conn *net.TCPConn, sessionKey []byte, localIdentity, remoteIdentity // The ciphertext that is actually transferred over the network is larger, so you // should not rely on this number as an indication of network metrics. func (c *Conn) Read(b []byte) (int, error) { - f := NewFrame(nil) + f := frame.NewFrame(nil) _, err := f.ReadFrom(c.conn) if err != nil { return 0, err } - plaintext, err := dcrypto.Decrypt(c.sessionKey, f.Contents()) + plaintext, err := encryption.Decrypt(c.sessionKey, f.Contents()) if err != nil { return 0, err } @@ -42,11 +43,11 @@ func (c *Conn) Read(b []byte) (int, error) { // Write implements io.Writer. func (c *Conn) Write(b []byte) (int, error) { - ciphertext, err := dcrypto.Encrypt(c.sessionKey, b) + ciphertext, err := encryption.Encrypt(c.sessionKey, b) if err != nil { return 0, err } - _, err = NewFrame(ciphertext).WriteTo(c.conn) + _, err = frame.NewFrame(ciphertext).WriteTo(c.conn) if err != nil { return 0, err } diff --git a/shared/dnet/listener.go b/pkg/network/listener.go similarity index 84% rename from shared/dnet/listener.go rename to pkg/network/listener.go index 75e5936..a3da2c1 100644 --- a/shared/dnet/listener.go +++ b/pkg/network/listener.go @@ -1,10 +1,12 @@ -package dnet +package network import ( "context" "crypto/ecdsa" "log/slog" "net" + + "koti.casa/numenor-labs/dsfx/pkg/handshake" ) // Listener ... @@ -23,7 +25,7 @@ func (l *Listener) Accept() (net.Conn, error) { return nil, err } - clientIdentity, sessionKey, err := AcceptHandshake(ctx, conn, l.identity) + clientIdentity, sessionKey, err := handshake.AcceptHandshake(ctx, conn, l.identity) if err != nil { return nil, err } diff --git a/shared/dnet/dnet.go b/pkg/network/network.go similarity index 75% rename from shared/dnet/dnet.go rename to pkg/network/network.go index 961ee26..0b0ed00 100644 --- a/shared/dnet/dnet.go +++ b/pkg/network/network.go @@ -1,11 +1,12 @@ -package dnet +package network import ( "context" "crypto/ecdsa" "net" - "koti.casa/numenor-labs/dsfx/shared/dlog" + "koti.casa/numenor-labs/dsfx/pkg/handshake" + "koti.casa/numenor-labs/dsfx/pkg/logging" ) // Dial ... @@ -20,7 +21,7 @@ func Dial( return nil, err } - sessionKey, err := Handshake(ctx, conn, identity, raddr.PublicKey()) + sessionKey, err := handshake.Handshake(ctx, conn, identity, raddr.PublicKey()) if err != nil { return nil, err } @@ -39,7 +40,7 @@ func Listen( return nil, err } - logger := dlog.FromContext(ctx) + logger := logging.FromContext(ctx) return &Listener{logger, tcpListener, identity}, nil } diff --git a/shared/dcrypto/hkdf.go b/shared/dcrypto/hkdf.go deleted file mode 100644 index 909cb11..0000000 --- a/shared/dcrypto/hkdf.go +++ /dev/null @@ -1,11 +0,0 @@ -package dcrypto - -import ( - "crypto/hkdf" - "crypto/sha256" -) - -// Key creates a key from the given secret and salt using HKDF. -func Key(secret, salt []byte) ([]byte, error) { - return hkdf.Key(sha256.New, secret, salt, "", 32) -} diff --git a/shared/dcrypto/rand.go b/shared/dcrypto/rand.go deleted file mode 100644 index b72b239..0000000 --- a/shared/dcrypto/rand.go +++ /dev/null @@ -1,12 +0,0 @@ -package dcrypto - -import "crypto/rand" - -// Challenge generates a random challenge of n bytes. -func Challenge(n int) []byte { - challenge := make([]byte, n) - if _, err := rand.Read(challenge); err != nil || len(challenge) != n { - return nil - } - return challenge -} diff --git a/cmd/genkey/main.go b/tool/genkey/main.go similarity index 80% rename from cmd/genkey/main.go rename to tool/genkey/main.go index 0e918b7..08be656 100644 --- a/cmd/genkey/main.go +++ b/tool/genkey/main.go @@ -6,11 +6,11 @@ import ( "fmt" "os" - "koti.casa/numenor-labs/dsfx/shared/dcrypto" + "koti.casa/numenor-labs/dsfx/pkg/crypto/identity" ) func main() { - key, err := dcrypto.GenerateSigningKey() + key, err := identity.GenerateSigningKey() if err != nil { panic(err) }