mirror of
https://github.com/FiloSottile/age.git
synced 2026-09-04 07:07:16 +00:00
cmd/age: reject malformed supported SSH recipients
Reported by Joe Doyle of Trail of Bits.
This commit is contained in:
@@ -117,6 +117,52 @@ func TestParseFileSizeLimit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnsupportedSSHKey(t *testing.T) {
|
||||
tests := []struct {
|
||||
name, key, want string
|
||||
ok bool
|
||||
}{
|
||||
{
|
||||
"truncated Ed25519",
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH9pO5pz22JZEasoS2LEWWvJiUYI9M6l1uZc31FG",
|
||||
"", false,
|
||||
},
|
||||
{
|
||||
"type-only RSA",
|
||||
"ssh-rsa AAAAB3NzaC1yc2E=",
|
||||
"", false,
|
||||
},
|
||||
{
|
||||
"small RSA",
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCxlfoYpG04TTnmjocggQZI5l0fjvaUMky1ZD5zwktDKAgBj441OAtFj/3m7Gujpxx/8w3jjJoUXVtU+NFbcC972ROpPI4aPq3OR5SmuuR5bCr6efeZlyAEMY/DmbkeUe1TrvdU3VMDGJAvEEzT1wzokKt9PiwbV2jNkdPjaG5LDQ==",
|
||||
"ssh-rsa", true,
|
||||
},
|
||||
{
|
||||
"invalid Ed25519 point",
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC",
|
||||
"", false,
|
||||
},
|
||||
{
|
||||
"unknown type",
|
||||
"totally-not-a-key AAAAEXRvdGFsbHktbm90LWEta2V5",
|
||||
"totally-not-a-key", true,
|
||||
},
|
||||
{
|
||||
"ECDSA",
|
||||
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOSh0Qo5zaBE33EiJnxwIBuZml9Nt9rCfvGi++FniWSk8X/10EO4E7KMgqGLlkCTDNIcbuKcsgfMw1/tifB8aTc=",
|
||||
"ecdsa-sha2-nistp256", true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, ok := unsupportedSSHKey(tt.key)
|
||||
if got != tt.want || ok != tt.ok {
|
||||
t.Errorf("unsupportedSSHKey() = %q, %v; want %q, %v", got, ok, tt.want, tt.ok)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var buildExtraCommands = sync.OnceValue(func() error {
|
||||
bindir := filepath.SplitList(os.Getenv("PATH"))[0]
|
||||
// Build age-keygen and age-plugin-pq into the test binary directory.
|
||||
|
||||
+23
-3
@@ -6,6 +6,7 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/rsa"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -88,7 +89,7 @@ func parseRecipientsFile(name string) ([]age.Recipient, error) {
|
||||
}
|
||||
r, err := parseRecipient(line)
|
||||
if err != nil {
|
||||
if t, ok := sshKeyType(line); ok {
|
||||
if t, unsupported := unsupportedSSHKey(line); unsupported {
|
||||
// Skip unsupported but valid SSH public keys with a warning.
|
||||
warningf("recipients file %q: ignoring unsupported SSH key of type %q at line %d", name, t, n)
|
||||
continue
|
||||
@@ -114,7 +115,7 @@ func parseRecipientsFile(name string) ([]age.Recipient, error) {
|
||||
return recs, nil
|
||||
}
|
||||
|
||||
func sshKeyType(s string) (string, bool) {
|
||||
func unsupportedSSHKey(s string) (string, bool) {
|
||||
// TODO: also ignore options? And maybe support multiple spaces and tabs as
|
||||
// field separators like OpenSSH?
|
||||
fields := strings.Split(s, " ")
|
||||
@@ -132,7 +133,26 @@ func sshKeyType(s string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
if t := fields[0]; t == string(typeBytes) {
|
||||
return t, true
|
||||
switch t {
|
||||
case "ssh-ed25519":
|
||||
return "", false
|
||||
case "ssh-rsa":
|
||||
out, _, _, _, err := ssh.ParseAuthorizedKey([]byte(s))
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
cryptoKey, ok := out.(ssh.CryptoPublicKey)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
rsaKey, ok := cryptoKey.CryptoPublicKey().(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
return t, rsaKey.N.BitLen() < 2048
|
||||
default:
|
||||
return t, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user