1package sshkey
2
3import (
4 "crypto/sha256"
5 "encoding/base64"
6 "encoding/binary"
7 "errors"
8 "slices"
9)
10
11const (
12 MaxPerAccount = 10
13 MinRSABits = 3072
14 MaxLineBytes = 16 << 10
15)
16
17var (
18 ErrNotOneLine = errors.New("sshkey: more than one line")
19 ErrNotAKey = errors.New("sshkey: not an OpenSSH public key")
20 ErrOptions = errors.New("sshkey: the line carries authorized_keys options")
21 ErrCertificate = errors.New("sshkey: a certificate, not a key")
22 ErrKeyType = errors.New("sshkey: the key type is not accepted")
23 ErrShortRSA = errors.New("sshkey: the RSA key is shorter than 3072 bits")
24 ErrLabel = errors.New("sshkey: the label is not valid")
25 ErrOwned = errors.New("sshkey: the key is on this account already")
26 ErrTaken = errors.New("sshkey: the key is on another account")
27 ErrLimit = errors.New("sshkey: the account holds as many keys as it may")
28)
29
30var Types = []string{
31 "ssh-ed25519",
32 "ecdsa-sha2-nistp256",
33 "ecdsa-sha2-nistp384",
34 "ecdsa-sha2-nistp521",
35 "ssh-rsa",
36 "sk-ssh-ed25519@openssh.com",
37 "sk-ecdsa-sha2-nistp256@openssh.com",
38}
39
40type Key struct {
41 Type string
42 Data []byte
43 Fingerprint string
44 Comment string
45}
46
47func (k Key) Authorized() string {
48 return Authorized(k.Type, k.Data)
49}
50
51func Authorized(keyType string, data []byte) string {
52 return keyType + " " + base64.StdEncoding.EncodeToString(data)
53}
54
55func Fingerprint(data []byte) string {
56 sum := sha256.Sum256(data)
57 return "SHA256:" + base64.RawStdEncoding.EncodeToString(sum[:])
58}
59
60func Check(keyType string, data []byte) error {
61 if !slices.Contains(Types, keyType) {
62 return ErrKeyType
63 }
64 n := len(keyType)
65 if len(data) <= 4+n || binary.BigEndian.Uint32(data) != uint32(n) || string(data[4:4+n]) != keyType {
66 return ErrNotAKey
67 }
68 return nil
69}