diff --git a/ssh/agent/client.go b/ssh/agent/client.go index 4276002..eb96184 100644 --- a/ssh/agent/client.go +++ b/ssh/agent/client.go @@ -621,6 +621,9 @@ func (c *client) insertKey(s interface{}, comment string, constraints []byte) er Constraints: constraints, }) case ed25519.PrivateKey: + if len(k) != ed25519.PrivateKeySize { + return fmt.Errorf("agent: bad ED25519 key size: %d", len(k)) + } req = ssh.Marshal(ed25519KeyMsg{ Type: ssh.KeyAlgoED25519, Pub: []byte(k)[32:], @@ -632,6 +635,9 @@ func (c *client) insertKey(s interface{}, comment string, constraints []byte) er // general idiom is to pass ed25519.PrivateKey by value, not by pointer. // We still support the pointer variant for backwards compatibility. case *ed25519.PrivateKey: + if len(*k) != ed25519.PrivateKeySize { + return fmt.Errorf("agent: bad ED25519 key size: %d", len(*k)) + } req = ssh.Marshal(ed25519KeyMsg{ Type: ssh.KeyAlgoED25519, Pub: []byte(*k)[32:], @@ -756,6 +762,9 @@ func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string Constraints: constraints, }) case ed25519.PrivateKey: + if len(k) != ed25519.PrivateKeySize { + return fmt.Errorf("agent: bad ED25519 key size: %d", len(k)) + } req = ssh.Marshal(ed25519CertMsg{ Type: cert.Type(), CertBytes: cert.Marshal(), @@ -768,6 +777,9 @@ func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string // general idiom is to pass ed25519.PrivateKey by value, not by pointer. // We still support the pointer variant for backwards compatibility. case *ed25519.PrivateKey: + if len(*k) != ed25519.PrivateKeySize { + return fmt.Errorf("agent: bad ED25519 key size: %d", len(*k)) + } req = ssh.Marshal(ed25519CertMsg{ Type: cert.Type(), CertBytes: cert.Marshal(), diff --git a/ssh/agent/client_test.go b/ssh/agent/client_test.go index 567c0d9..3b925c3 100644 --- a/ssh/agent/client_test.go +++ b/ssh/agent/client_test.go @@ -6,6 +6,7 @@ package agent import ( "bytes" + "crypto/ed25519" "crypto/rand" "encoding/binary" "errors" @@ -654,3 +655,36 @@ func TestAddConstraintExtensionsWireFormat(t *testing.T) { } } } + +func TestAddRejectsShortEd25519Key(t *testing.T) { + short := ed25519.PrivateKey(make([]byte, 16)) + // insertCert reaches its ed25519 branch only when AddedKey.Certificate + // is non-nil. The length check returns before cert.Type/Marshal run, so + // an empty certificate is sufficient to drive the path. + emptyCert := &ssh.Certificate{} + + cases := []struct { + name string + add AddedKey + }{ + {"insertKey value", AddedKey{PrivateKey: short}}, + {"insertKey pointer", AddedKey{PrivateKey: &short}}, + {"insertCert value", AddedKey{PrivateKey: short, Certificate: emptyCert}}, + {"insertCert pointer", AddedKey{PrivateKey: &short, Certificate: emptyCert}}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + client, cleanup := startKeyringAgent(t) + defer cleanup() + + err := client.Add(tc.add) + if err == nil { + t.Fatal("Add accepted ed25519 key shorter than 64 bytes") + } + if !strings.Contains(err.Error(), "bad ED25519 key size") { + t.Errorf("got error %q, want substring %q", err.Error(), "bad ED25519 key size") + } + }) + } +}