From 6bc1ce6679b2d70d303a76d9fa06849d390f0ac2 Mon Sep 17 00:00:00 2001 From: cheddar Date: Fri, 14 Feb 2025 19:41:22 -0500 Subject: [PATCH] Implement test server --- client/client.go | 1 + main.go | 53 +++++++++++++++++++++++++++++++++----- server/key_directory.go | 40 +++++++++++++++++++++++++++++ server/server.go | 56 +++++++++++++++++++++++++++++++++++++++++ testkey2 | 7 ++++++ testkey2.pub | 1 + 6 files changed, 152 insertions(+), 6 deletions(-) create mode 100644 server/key_directory.go create mode 100644 server/server.go create mode 100644 testkey2 create mode 100644 testkey2.pub diff --git a/client/client.go b/client/client.go index ba33d12..b470b5f 100644 --- a/client/client.go +++ b/client/client.go @@ -31,6 +31,7 @@ func GetSigningClient(key crypto.PrivateKey, keyId string) (*http.Client, error) } client := httpsig.NewClient(httpsig.ClientOpts{ + Tag: "test-tag", KeyID: keyId, Alg: alg, }) diff --git a/main.go b/main.go index 2c3f6cb..22690d9 100644 --- a/main.go +++ b/main.go @@ -4,22 +4,38 @@ import ( "bytes" "crypto" "encoding/json" + "flag" "fmt" + "io" "log" + "net/http" "os" "crispbyte.dev/sig-auth/client" + "crispbyte.dev/sig-auth/server" "github.com/opencontainers/go-digest" "golang.org/x/crypto/ssh" ) func main() { + useClient := flag.Bool("c", false, "Run client") + + keyPath := flag.String("key", "", "Path to the private (client mode) or public (server mode) to use") + + flag.Parse() + + if *useClient { + runClient(keyPath) + } else { + runServer(keyPath) + } +} + +func runClient(keyFile *string) { testData := map[string]string{"hello": "world"} json_data, _ := json.Marshal(testData) - keyFile := "testkey" - - key, err := loadPrivateKey(keyFile) + key, err := loadPrivateKey(*keyFile) if err != nil { log.Fatal(err) @@ -50,11 +66,24 @@ func main() { defer resp.Body.Close() - var res map[string]interface{} + out, err := io.ReadAll(resp.Body) - json.NewDecoder(resp.Body).Decode(&res) + if err != nil { + log.Fatal(err) + } - fmt.Println(res) + fmt.Println(resp.StatusCode) + fmt.Println(string(out[:])) +} + +func runServer(keyFile *string) { + key, err := loadPublicKey(*keyFile) + + if err != nil { + log.Fatal(err) + } + + server.Start(key) } func loadPrivateKey(keyFile string) (crypto.PrivateKey, error) { @@ -66,3 +95,15 @@ func loadPrivateKey(keyFile string) (crypto.PrivateKey, error) { return ssh.ParseRawPrivateKey(keyBytes) } + +func loadPublicKey(keyFile string) (crypto.PublicKey, error) { + keyBytes, err := os.ReadFile(keyFile) + + if err != nil { + return nil, err + } + + pk, _, _, _, err := ssh.ParseAuthorizedKey(keyBytes) + + return pk.(ssh.CryptoPublicKey).CryptoPublicKey(), err +} diff --git a/server/key_directory.go b/server/key_directory.go new file mode 100644 index 0000000..926fe02 --- /dev/null +++ b/server/key_directory.go @@ -0,0 +1,40 @@ +package server + +import ( + "context" + "crypto" + "crypto/ed25519" + "fmt" + + "github.com/common-fate/httpsig/alg_ed25519" + "github.com/common-fate/httpsig/verifier" +) + +type KeyEntry struct { + alg string + publicKey crypto.PublicKey + userId string +} + +type InMemoryDirectory struct { + records map[string]KeyEntry +} + +func (dir *InMemoryDirectory) GetKey(ctx context.Context, keyId string, _ string) (verifier.Algorithm, error) { + entry := dir.records[keyId] + + var alg verifier.Algorithm + var err error + + switch entry.alg { + case "ed25519": + alg = alg_ed25519.Ed25519{ + PublicKey: entry.publicKey.(ed25519.PublicKey), + Attrs: entry.userId, + } + default: + err = fmt.Errorf("unknown algoritm: %s", entry.alg) + } + + return alg, err +} diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..a323ecc --- /dev/null +++ b/server/server.go @@ -0,0 +1,56 @@ +package server + +import ( + "context" + "crypto" + "fmt" + "net/http" + + "github.com/common-fate/httpsig" + "github.com/common-fate/httpsig/inmemory" +) + +func Start(publicKey crypto.PublicKey) error { + keyDir := InMemoryDirectory{ + records: map[string]KeyEntry{}, + } + + keyId := "test-id" + + keyDir.records[keyId] = KeyEntry{ + alg: "ed25519", + publicKey: publicKey, + userId: "test_user", + } + + mux := http.NewServeMux() + + verifier := httpsig.Middleware(httpsig.MiddlewareOpts{ + NonceStorage: inmemory.NewNonceStorage(), + KeyDirectory: &keyDir, + Tag: "test-tag", + Scheme: "http", + Authority: "localhost:8080", + + OnValidationError: func(ctx context.Context, err error) { + fmt.Printf("validation error: %s\n", err) + }, + + OnDeriveSigningString: func(ctx context.Context, stringToSign string) { + fmt.Printf("string to sign:\n%s\n", stringToSign) + }, + }) + + mux.Handle("/", verifier(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Printf("Responding...\n") + attr := httpsig.AttributesFromContext(r.Context()).(string) + fmt.Printf("User is %s\n", attr) + msg := fmt.Sprintf("hello, %s!", attr) + w.Write([]byte(msg)) + fmt.Printf("Responded...\n") + }))) + + err := http.ListenAndServe("localhost:8080", mux) + + return err +} diff --git a/testkey2 b/testkey2 new file mode 100644 index 0000000..7e16e11 --- /dev/null +++ b/testkey2 @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACAVBfVj2Gf8IBbU9G8nYz9Y6UQRcjdocl3CD0GKhIAt5wAAAJA3GdxYNxnc +WAAAAAtzc2gtZWQyNTUxOQAAACAVBfVj2Gf8IBbU9G8nYz9Y6UQRcjdocl3CD0GKhIAt5w +AAAECcClGiCCayTB0yRGxnn3R26heCf966qN+YAISC4dCMERUF9WPYZ/wgFtT0bydjP1jp +RBFyN2hyXcIPQYqEgC3nAAAADGphbWllQGF0aGVuYQE= +-----END OPENSSH PRIVATE KEY----- diff --git a/testkey2.pub b/testkey2.pub new file mode 100644 index 0000000..39ae938 --- /dev/null +++ b/testkey2.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBUF9WPYZ/wgFtT0bydjP1jpRBFyN2hyXcIPQYqEgC3n jamie@athena