Raw
 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}