auth.go 1.84 KB
Newer Older
1
package auth
Patrick Devine's avatar
Patrick Devine committed
2
3
4

import (
	"bytes"
5
	"context"
Patrick Devine's avatar
Patrick Devine committed
6
7
8
9
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
10
	"log/slog"
Patrick Devine's avatar
Patrick Devine committed
11
	"os"
Michael Yang's avatar
Michael Yang committed
12
	"path/filepath"
13
	"strings"
Patrick Devine's avatar
Patrick Devine committed
14
15

	"golang.org/x/crypto/ssh"
16
17
)

Michael Yang's avatar
Michael Yang committed
18
const defaultPrivateKey = "id_ed25519"
Patrick Devine's avatar
Patrick Devine committed
19

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
func keyPath() (string, error) {
	home, err := os.UserHomeDir()
	if err != nil {
		return "", err
	}

	return filepath.Join(home, ".ollama", defaultPrivateKey), nil
}

func GetPublicKey() (string, error) {
	keyPath, err := keyPath()
	if err != nil {
		return "", err
	}

	privateKeyFile, err := os.ReadFile(keyPath)
	if err != nil {
		slog.Info(fmt.Sprintf("Failed to load private key: %v", err))
		return "", err
	}

	privateKey, err := ssh.ParsePrivateKey(privateKeyFile)
	if err != nil {
		return "", err
	}

	publicKey := ssh.MarshalAuthorizedKey(privateKey.PublicKey())

	return strings.TrimSpace(string(publicKey)), nil
}

Michael Yang's avatar
Michael Yang committed
51
func NewNonce(r io.Reader, length int) (string, error) {
Patrick Devine's avatar
Patrick Devine committed
52
	nonce := make([]byte, length)
Michael Yang's avatar
Michael Yang committed
53
	if _, err := io.ReadFull(r, nonce); err != nil {
Patrick Devine's avatar
Patrick Devine committed
54
55
		return "", err
	}
Michael Yang's avatar
Michael Yang committed
56

Michael Yang's avatar
Michael Yang committed
57
	return base64.RawURLEncoding.EncodeToString(nonce), nil
Patrick Devine's avatar
Patrick Devine committed
58
59
}

Michael Yang's avatar
Michael Yang committed
60
func Sign(ctx context.Context, bts []byte) (string, error) {
61
	keyPath, err := keyPath()
Patrick Devine's avatar
Patrick Devine committed
62
	if err != nil {
Michael Yang's avatar
Michael Yang committed
63
		return "", err
Patrick Devine's avatar
Patrick Devine committed
64
	}
Michael Yang's avatar
Michael Yang committed
65

Michael Yang's avatar
Michael Yang committed
66
	privateKeyFile, err := os.ReadFile(keyPath)
Patrick Devine's avatar
Patrick Devine committed
67
	if err != nil {
Michael Yang's avatar
Michael Yang committed
68
		slog.Info(fmt.Sprintf("Failed to load private key: %v", err))
Patrick Devine's avatar
Patrick Devine committed
69
70
71
		return "", err
	}

Michael Yang's avatar
Michael Yang committed
72
	privateKey, err := ssh.ParsePrivateKey(privateKeyFile)
Patrick Devine's avatar
Patrick Devine committed
73
74
75
76
77
	if err != nil {
		return "", err
	}

	// get the pubkey, but remove the type
Michael Yang's avatar
Michael Yang committed
78
79
	publicKey := ssh.MarshalAuthorizedKey(privateKey.PublicKey())
	parts := bytes.Split(publicKey, []byte(" "))
Patrick Devine's avatar
Patrick Devine committed
80
81
82
83
	if len(parts) < 2 {
		return "", fmt.Errorf("malformed public key")
	}

Michael Yang's avatar
Michael Yang committed
84
	signedData, err := privateKey.Sign(rand.Reader, bts)
Patrick Devine's avatar
Patrick Devine committed
85
86
87
88
89
	if err != nil {
		return "", err
	}

	// signature is <pubkey>:<signature>
Michael Yang's avatar
Michael Yang committed
90
	return fmt.Sprintf("%s:%s", bytes.TrimSpace(parts[1]), base64.StdEncoding.EncodeToString(signedData.Blob)), nil
Patrick Devine's avatar
Patrick Devine committed
91
}