- commit
- 949d1fc
- parent
- 3dfe5b8
- author
- cheddar
- date
- 2025-02-18 02:50:48 +0100 CET
Add basic key registration
6 files changed,
+138,
-55
+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 }
+6,
-0
1@@ -0,0 +1,6 @@
2+package server
3+
4+type RegisterRequest struct {
5+ UserId string
6+ Key string
7+}
+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+}
+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 }
+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+}