return c.transport.reader.setupKeys(serverKeys, K, H, H, hashFunc)
 }
 
-// authenticate authenticates with the remote server. See RFC 4252. 
-// Only "password" authentication is supported.
-func (c *ClientConn) authenticate() error {
-       if err := c.writePacket(marshal(msgServiceRequest, serviceRequestMsg{serviceUserAuth})); err != nil {
-               return err
-       }
-       packet, err := c.readPacket()
-       if err != nil {
-               return err
-       }
-
-       var serviceAccept serviceAcceptMsg
-       if err = unmarshal(&serviceAccept, packet, msgServiceAccept); err != nil {
-               return err
-       }
-
-       // TODO(dfc) support proper authentication method negotation
-       method := "none"
-       if c.config.Password != "" {
-               method = "password"
-       }
-       if err := c.sendUserAuthReq(method); err != nil {
-               return err
-       }
-
-       if packet, err = c.readPacket(); err != nil {
-               return err
-       }
-
-       if packet[0] != msgUserAuthSuccess {
-               return UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
-       }
-       return nil
-}
-
-func (c *ClientConn) sendUserAuthReq(method string) error {
-       length := stringLength([]byte(c.config.Password)) + 1
-       payload := make([]byte, length)
-       // always false for password auth, see RFC 4252 Section 8.
-       payload[0] = 0
-       marshalString(payload[1:], []byte(c.config.Password))
-
-       return c.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{
-               User:    c.config.User,
-               Service: serviceSSH,
-               Method:  method,
-               Payload: payload,
-       }))
-}
-
 // kexDH performs Diffie-Hellman key agreement on a ClientConn. The
 // returned values are given the same names as in RFC 4253, section 8.
 func (c *ClientConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handshakeMagics, hostKeyAlgo string) ([]byte, []byte, error) {
        // The username to authenticate.
        User string
 
-       // Used for "password" method authentication.
-       Password string
+       // A slice of ClientAuth methods. Only the first instance 
+       // of a particular RFC 4252 method will be used during authentication.
+       Auth []ClientAuth
 }
 
 func (c *ClientConfig) rand() io.Reader {
 
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ssh
+
+import (
+       "errors"
+)
+
+// authenticate authenticates with the remote server. See RFC 4252. 
+func (c *ClientConn) authenticate() error {
+       // initiate user auth session
+       if err := c.writePacket(marshal(msgServiceRequest, serviceRequestMsg{serviceUserAuth})); err != nil {
+               return err
+       }
+       packet, err := c.readPacket()
+       if err != nil {
+               return err
+       }
+       var serviceAccept serviceAcceptMsg
+       if err := unmarshal(&serviceAccept, packet, msgServiceAccept); err != nil {
+               return err
+       }
+       // during the authentication phase the client first attempts the "none" method
+       // then any untried methods suggested by the server. 
+       tried, remain := make(map[string]bool), make(map[string]bool)
+       for auth := ClientAuth(new(noneAuth)); auth != nil; {
+               ok, methods, err := auth.auth(c.config.User, c.transport)
+               if err != nil {
+                       return err
+               }
+               if ok {
+                       // success
+                       return nil
+               }
+               tried[auth.method()] = true
+               delete(remain, auth.method())
+               for _, meth := range methods {
+                       if tried[meth] {
+                               // if we've tried meth already, skip it.
+                               continue
+                       }
+                       remain[meth] = true
+               }
+               auth = nil
+               for _, a := range c.config.Auth {
+                       if remain[a.method()] {
+                               auth = a
+                               break
+                       }
+               }
+       }
+       return errors.New("ssh: unable to authenticate, no supported methods remain")
+}
+
+// A ClientAuth represents an instance of an RFC 4252 authentication method.
+type ClientAuth interface {
+       // auth authenticates user over transport t. 
+       // Returns true if authentication is successful.
+       // If authentication is not successful, a []string of alternative 
+       // method names is returned.
+       auth(user string, t *transport) (bool, []string, error)
+
+       // method returns the RFC 4252 method name.
+       method() string
+}
+
+// "none" authentication, RFC 4252 section 5.2.
+type noneAuth int
+
+func (n *noneAuth) auth(user string, t *transport) (bool, []string, error) {
+       if err := t.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{
+               User:    user,
+               Service: serviceSSH,
+               Method:  "none",
+       })); err != nil {
+               return false, nil, err
+       }
+
+       packet, err := t.readPacket()
+       if err != nil {
+               return false, nil, err
+       }
+
+       switch packet[0] {
+       case msgUserAuthSuccess:
+               return true, nil, nil
+       case msgUserAuthFailure:
+               msg := decode(packet).(*userAuthFailureMsg)
+               return false, msg.Methods, nil
+       }
+       return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
+}
+
+func (n *noneAuth) method() string {
+       return "none"
+}
+
+// "password" authentication, RFC 4252 Section 8.
+type passwordAuth struct {
+       ClientPassword
+}
+
+func (p *passwordAuth) auth(user string, t *transport) (bool, []string, error) {
+       type passwordAuthMsg struct {
+               User     string
+               Service  string
+               Method   string
+               Reply    bool
+               Password string
+       }
+
+       pw, err := p.Password(user)
+       if err != nil {
+               return false, nil, err
+       }
+
+       if err := t.writePacket(marshal(msgUserAuthRequest, passwordAuthMsg{
+               User:     user,
+               Service:  serviceSSH,
+               Method:   "password",
+               Reply:    false,
+               Password: pw,
+       })); err != nil {
+               return false, nil, err
+       }
+
+       packet, err := t.readPacket()
+       if err != nil {
+               return false, nil, err
+       }
+
+       switch packet[0] {
+       case msgUserAuthSuccess:
+               return true, nil, nil
+       case msgUserAuthFailure:
+               msg := decode(packet).(*userAuthFailureMsg)
+               return false, msg.Methods, nil
+       }
+       return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
+}
+
+func (p *passwordAuth) method() string {
+       return "password"
+}
+
+// A ClientPassword implements access to a client's passwords.
+type ClientPassword interface {
+       // Password returns the password to use for user.
+       Password(user string) (password string, err error)
+}
+
+// ClientAuthPassword returns a ClientAuth using password authentication.
+func ClientAuthPassword(impl ClientPassword) ClientAuth {
+       return &passwordAuth{impl}
+}