diff options
Diffstat (limited to 'models/publickey.go')
-rw-r--r-- | models/publickey.go | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/models/publickey.go b/models/publickey.go index fbba691abc..41233d0c39 100644 --- a/models/publickey.go +++ b/models/publickey.go @@ -6,6 +6,8 @@ package models import ( "bufio" + "encoding/base64" + "encoding/binary" "errors" "fmt" "io" @@ -111,6 +113,85 @@ var ( } ) +func extractTypeFromBase64Key(key string) (string, error) { + b, err := base64.StdEncoding.DecodeString(key) + if err != nil || len(b) < 4 { + return "", errors.New("Invalid key format") + } + + keyLength := int(binary.BigEndian.Uint32(b)) + + if len(b) < 4+keyLength { + return "", errors.New("Invalid key format") + } + + return string(b[4 : 4+keyLength]), nil +} + +// Parse any key string in openssh or ssh2 format to clean openssh string (rfc4253) +func ParseKeyString(content string) (string, error) { + + // Transform all legal line endings to a single "\n" + s := strings.Replace(strings.Replace(strings.TrimSpace(content), "\r\n", "\n", -1), "\r", "\n", -1) + + lines := strings.Split(s, "\n") + + var keyType, keyContent, keyComment string + + if len(lines) == 1 { + // Parse openssh format + parts := strings.Fields(lines[0]) + switch len(parts) { + case 0: + return "", errors.New("Empty key") + case 1: + keyContent = parts[0] + case 2: + keyType = parts[0] + keyContent = parts[1] + default: + keyType = parts[0] + keyContent = parts[1] + keyComment = parts[2] + } + + // If keyType is not given, extract it from content. If given, validate it + if len(keyType) == 0 { + if t, err := extractTypeFromBase64Key(keyContent); err == nil { + keyType = t + } else { + return "", err + } + } else { + if t, err := extractTypeFromBase64Key(keyContent); err != nil || keyType != t { + return "", err + } + } + } else { + // Parse SSH2 file format. + continuationLine := false + + for _, line := range lines { + // Skip lines that: + // 1) are a continuation of the previous line, + // 2) contain ":" as that are comment lines + // 3) contain "-" as that are begin and end tags + if continuationLine || strings.ContainsAny(line, ":-") { + continuationLine = strings.HasSuffix(line, "\\") + } else { + keyContent = keyContent + line + } + } + + if t, err := extractTypeFromBase64Key(keyContent); err == nil { + keyType = t + } else { + return "", err + } + } + return keyType + " " + keyContent + " " + keyComment, nil +} + // CheckPublicKeyString checks if the given public key string is recognized by SSH. func CheckPublicKeyString(content string) (bool, error) { content = strings.TrimRight(content, "\n\r") |