sig-auth.git

git clone https://git.crispbyte.dev/sig-auth.git

commit
949d1fc
parent
3dfe5b8
author
cheddar
date
2025-02-18 02:50:48 +0100 CET
Add basic key registration
6 files changed,  +138, -55
A keydirectory/registration.go
+12, -0
 1@@ -0,0 +1,12 @@
 2+package keydirectory
 3+
 4+import (
 5+	"crypto"
 6+
 7+	"github.com/common-fate/httpsig/verifier"
 8+)
 9+
10+type RegistrationDirectory interface {
11+	verifier.KeyDirectory
12+	RegisterKey(key crypto.PublicKey, alg string, userId string) (string, error)
13+}
M main.go
+33, -28
  1@@ -21,35 +21,46 @@ import (
  2 func main() {
  3 	useClient := flag.Bool("c", false, "Run client")
  4 
  5-	keyPath := flag.String("key", "", "Path to the private key (client mode) or public key (server mode) to use - Required")
  6+	register := flag.Bool("r", false, "Register a key")
  7+
  8+	user := flag.String("user", "", "Username to register")
  9+
 10+	keyPath := flag.String("key", "", "Path to the private key (client mode) or public key (registration mode) to use")
 11 
 12 	simulateCaddy := flag.Bool("caddy", false, "Simulate caddy reverse proxy")
 13 
 14 	flag.Parse()
 15 
 16-	if *keyPath == "" {
 17-		flag.PrintDefaults()
 18-		return
 19-	}
 20-
 21 	if *useClient {
 22-		runClient(keyPath, *simulateCaddy)
 23+		if *keyPath == "" || *user == "" {
 24+			flag.PrintDefaults()
 25+			return
 26+		}
 27+
 28+		runClient(*keyPath, *user, *simulateCaddy)
 29+	} else if *register {
 30+		if *keyPath == "" || *user == "" {
 31+			flag.PrintDefaults()
 32+			return
 33+		}
 34+
 35+		registerKey(*keyPath, *user)
 36 	} else {
 37-		runServer(keyPath, *simulateCaddy)
 38+		runServer(*simulateCaddy)
 39 	}
 40 }
 41 
 42-func runClient(keyFile *string, simulateCaddy bool) {
 43+func runClient(keyFile string, user string, simulateCaddy bool) {
 44 	testData := map[string]string{"hello": "world"}
 45 	json_data, _ := json.Marshal(testData)
 46 
 47-	key, err := loadPrivateKey(*keyFile)
 48+	key, err := loadPrivateKey(keyFile)
 49 
 50 	if err != nil {
 51 		log.Fatal(err)
 52 	}
 53 
 54-	client, err := client.GetSigningClient(key, "test-id")
 55+	client, err := client.GetSigningClient(key, user)
 56 
 57 	if err != nil {
 58 		log.Fatal(err)
 59@@ -92,14 +103,8 @@ func runClient(keyFile *string, simulateCaddy bool) {
 60 	fmt.Println(string(out[:]))
 61 }
 62 
 63-func runServer(keyFile *string, simulateCaddy bool) {
 64-	key, alg, err := loadPublicKey(*keyFile)
 65-
 66-	if err != nil {
 67-		log.Fatal(err)
 68-	}
 69-
 70-	keyDir := sqlite_directory.CreateDirectory(alg, key)
 71+func runServer(simulateCaddy bool) {
 72+	keyDir := sqlite_directory.CreateDirectory()
 73 
 74 	server.Start(simulateCaddy, keyDir)
 75 }
 76@@ -114,21 +119,21 @@ func loadPrivateKey(keyFile string) (crypto.PrivateKey, error) {
 77 	return ssh.ParseRawPrivateKey(keyBytes)
 78 }
 79 
 80-func loadPublicKey(keyFile string) (crypto.PublicKey, string, error) {
 81+func registerKey(keyFile string, userId string) {
 82 	keyBytes, err := os.ReadFile(keyFile)
 83 
 84 	if err != nil {
 85-		return nil, "", err
 86+		log.Fatal(err)
 87 	}
 88 
 89-	pk, _, _, _, err := ssh.ParseAuthorizedKey(keyBytes)
 90-
 91-	var alg string
 92+	keyText := string(keyBytes)
 93 
 94-	switch pk.Type() {
 95-	case "ssh-ed25519":
 96-		alg = "ed25519"
 97+	request := server.RegisterRequest{
 98+		UserId: userId,
 99+		Key:    keyText,
100 	}
101 
102-	return pk.(ssh.CryptoPublicKey).CryptoPublicKey(), alg, err
103+	json_data, _ := json.Marshal(request)
104+
105+	http.DefaultClient.Post("http://localhost:8080/register", "application/json", bytes.NewBuffer(json_data))
106 }
A server/register_request.go
+6, -0
1@@ -0,0 +1,6 @@
2+package server
3+
4+type RegisterRequest struct {
5+	UserId string
6+	Key    string
7+}
M server/server.go
+66, -12
  1@@ -2,15 +2,18 @@ package server
  2 
  3 import (
  4 	"context"
  5+	"crypto"
  6+	"encoding/json"
  7 	"fmt"
  8 	"net/http"
  9 
 10+	"crispbyte.dev/sig-auth/keydirectory"
 11 	"github.com/common-fate/httpsig"
 12 	"github.com/common-fate/httpsig/inmemory"
 13-	"github.com/common-fate/httpsig/verifier"
 14+	"golang.org/x/crypto/ssh"
 15 )
 16 
 17-func Start(isCaddyAuth bool, keyDir verifier.KeyDirectory) error {
 18+func Start(isCaddyAuth bool, keyDir keydirectory.RegistrationDirectory) error {
 19 	mux := http.NewServeMux()
 20 
 21 	verifier := httpsig.Middleware(httpsig.MiddlewareOpts{
 22@@ -29,16 +32,7 @@ func Start(isCaddyAuth bool, keyDir verifier.KeyDirectory) error {
 23 		},
 24 	})
 25 
 26-	verifyHandler := verifier(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 27-		attr := httpsig.AttributesFromContext(r.Context()).(string)
 28-
 29-		if isCaddyAuth {
 30-			w.Header().Add("Remote-User", attr)
 31-		} else {
 32-			msg := fmt.Sprintf("hello, %s!", attr)
 33-			w.Write([]byte(msg))
 34-		}
 35-	}))
 36+	verifyHandler := verifier(getDefaultHandler(isCaddyAuth))
 37 
 38 	var handler http.Handler
 39 
 40@@ -49,8 +43,68 @@ func Start(isCaddyAuth bool, keyDir verifier.KeyDirectory) error {
 41 	}
 42 
 43 	mux.Handle("/", handler)
 44+	mux.Handle("/register", getRegistrationHandler(keyDir))
 45 
 46 	err := http.ListenAndServe("localhost:8080", mux)
 47 
 48 	return err
 49 }
 50+
 51+func getDefaultHandler(isCaddyAuth bool) http.Handler {
 52+	handler := func(w http.ResponseWriter, r *http.Request) {
 53+		attr := httpsig.AttributesFromContext(r.Context()).(string)
 54+
 55+		if isCaddyAuth {
 56+			w.Header().Add("Remote-User", attr)
 57+		} else {
 58+			msg := fmt.Sprintf("hello, %s!", attr)
 59+			w.Write([]byte(msg))
 60+		}
 61+	}
 62+
 63+	return http.HandlerFunc(handler)
 64+}
 65+
 66+func getRegistrationHandler(keyDir keydirectory.RegistrationDirectory) http.Handler {
 67+	handler := func(w http.ResponseWriter, r *http.Request) {
 68+		if r.Method != "POST" {
 69+			http.Error(w, "Bad request", 400)
 70+			return
 71+		}
 72+
 73+		var request RegisterRequest
 74+
 75+		err := json.NewDecoder(r.Body).Decode(&request)
 76+
 77+		if err != nil {
 78+			http.Error(w, fmt.Sprintf("Bad request - %s", err), 400)
 79+			return
 80+		}
 81+
 82+		key, alg, err := parsePublicKey(request.Key)
 83+
 84+		if err != nil {
 85+			http.Error(w, fmt.Sprintf("Bad request - %s", err), 400)
 86+			return
 87+		}
 88+
 89+		fmt.Printf("Registering %s key for %s\n", alg, request.UserId)
 90+
 91+		keyDir.RegisterKey(key, alg, request.UserId)
 92+	}
 93+
 94+	return http.HandlerFunc(handler)
 95+}
 96+
 97+func parsePublicKey(input string) (crypto.PublicKey, string, error) {
 98+	pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(input))
 99+
100+	var alg string
101+
102+	switch pk.Type() {
103+	case "ssh-ed25519":
104+		alg = "ed25519"
105+	}
106+
107+	return pk.(ssh.CryptoPublicKey).CryptoPublicKey(), alg, err
108+}
M sqlite_directory/create_directory.go
+2, -14
 1@@ -1,23 +1,11 @@
 2 package sqlite_directory
 3 
 4 import (
 5-	"crypto"
 6-
 7 	"crispbyte.dev/sig-auth/keydirectory"
 8 )
 9 
10-func CreateDirectory(alg string, publicKey crypto.PublicKey) InMemoryDirectory {
11-	keyDir := InMemoryDirectory{
12+func CreateDirectory() InMemoryDirectory {
13+	return InMemoryDirectory{
14 		records: map[string]keydirectory.KeyEntry{},
15 	}
16-
17-	keyId := "test-id"
18-
19-	keyDir.records[keyId] = keydirectory.KeyEntry{
20-		Alg:       alg,
21-		PublicKey: publicKey,
22-		UserId:    "test_user",
23-	}
24-
25-	return keyDir
26 }
M sqlite_directory/sqlite_directory.go
+19, -1
 1@@ -2,7 +2,9 @@ package sqlite_directory
 2 
 3 import (
 4 	"context"
 5+	"crypto"
 6 	"crypto/ed25519"
 7+	"errors"
 8 	"fmt"
 9 
10 	"github.com/common-fate/httpsig/alg_ed25519"
11@@ -16,7 +18,11 @@ type InMemoryDirectory struct {
12 }
13 
14 func (dir InMemoryDirectory) GetKey(ctx context.Context, keyId string, _ string) (verifier.Algorithm, error) {
15-	entry := dir.records[keyId]
16+	entry, ok := dir.records[keyId]
17+
18+	if !ok {
19+		return nil, errors.New("key not found in directory")
20+	}
21 
22 	var alg verifier.Algorithm
23 	var err error
24@@ -33,3 +39,15 @@ func (dir InMemoryDirectory) GetKey(ctx context.Context, keyId string, _ string)
25 
26 	return alg, err
27 }
28+
29+func (dir InMemoryDirectory) RegisterKey(key crypto.PublicKey, alg string, userId string) (string, error) {
30+	keyId := userId
31+
32+	dir.records[keyId] = keydirectory.KeyEntry{
33+		Alg:       alg,
34+		PublicKey: key,
35+		UserId:    userId,
36+	}
37+
38+	return keyId, nil
39+}