package openssh import ( "bytes" "crypto/rsa" "strings" "golang.org/x/crypto/ssh" "tug/internal/platform/sshkey" ) func Parse(text string) (sshkey.Key, error) { line := strings.TrimSpace(text) switch { case len(line) > sshkey.MaxLineBytes: return sshkey.Key{}, sshkey.ErrNotAKey case strings.ContainsAny(line, "\r\n"): return sshkey.Key{}, sshkey.ErrNotOneLine case line == "" || line[0] == '#': return sshkey.Key{}, sshkey.ErrNotAKey } pub, comment, options, rest, err := ssh.ParseAuthorizedKey([]byte(line)) switch { case err != nil: return sshkey.Key{}, sshkey.ErrNotAKey case len(rest) > 0: return sshkey.Key{}, sshkey.ErrNotOneLine case len(options) > 0: return sshkey.Key{}, sshkey.ErrOptions } k, err := accept(pub) if err != nil { return sshkey.Key{}, err } k.Comment = comment return k, nil } func FromData(keyType string, data []byte) (sshkey.Key, error) { pub, err := ssh.ParsePublicKey(data) if err != nil || pub.Type() != keyType || !bytes.Equal(pub.Marshal(), data) { return sshkey.Key{}, sshkey.ErrNotAKey } return accept(pub) } func accept(pub ssh.PublicKey) (sshkey.Key, error) { if _, ok := pub.(*ssh.Certificate); ok || strings.HasSuffix(pub.Type(), "-cert-v01@openssh.com") { return sshkey.Key{}, sshkey.ErrCertificate } data := pub.Marshal() if err := sshkey.Check(pub.Type(), data); err != nil { return sshkey.Key{}, err } if pub.Type() == ssh.KeyAlgoRSA { crypto, ok := pub.(ssh.CryptoPublicKey) if !ok { return sshkey.Key{}, sshkey.ErrNotAKey } rsaKey, ok := crypto.CryptoPublicKey().(*rsa.PublicKey) if !ok { return sshkey.Key{}, sshkey.ErrNotAKey } if rsaKey.N.BitLen() < sshkey.MinRSABits { return sshkey.Key{}, sshkey.ErrShortRSA } } return sshkey.Key{Type: pub.Type(), Data: data, Fingerprint: sshkey.Fingerprint(data)}, nil }