123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789 |
- package agent
- import (
- "bytes"
- "crypto/dsa"
- "crypto/ecdsa"
- "crypto/elliptic"
- "crypto/rsa"
- "encoding/base64"
- "encoding/binary"
- "errors"
- "fmt"
- "io"
- "math/big"
- "sync"
- "crypto"
- "golang.org/x/crypto/ed25519"
- "golang.org/x/crypto/ssh"
- )
- type SignatureFlags uint32
- const (
- SignatureFlagReserved SignatureFlags = 1 << iota
- SignatureFlagRsaSha256
- SignatureFlagRsaSha512
- )
- type Agent interface {
-
- List() ([]*Key, error)
-
-
- Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error)
-
- Add(key AddedKey) error
-
- Remove(key ssh.PublicKey) error
-
- RemoveAll() error
-
- Lock(passphrase []byte) error
-
- Unlock(passphrase []byte) error
-
- Signers() ([]ssh.Signer, error)
- }
- type ExtendedAgent interface {
- Agent
-
- SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error)
-
-
-
-
-
-
-
-
-
-
-
- Extension(extensionType string, contents []byte) ([]byte, error)
- }
- type ConstraintExtension struct {
-
-
-
- ExtensionName string
-
-
- ExtensionDetails []byte
- }
- type AddedKey struct {
-
-
- PrivateKey interface{}
-
-
- Certificate *ssh.Certificate
-
- Comment string
-
-
- LifetimeSecs uint32
-
-
- ConfirmBeforeUse bool
-
-
- ConstraintExtensions []ConstraintExtension
- }
- const (
- agentRequestV1Identities = 1
- agentRemoveAllV1Identities = 9
-
- agentAddIdentity = 17
- agentRemoveIdentity = 18
- agentRemoveAllIdentities = 19
- agentAddIDConstrained = 25
-
- agentAddSmartcardKey = 20
- agentRemoveSmartcardKey = 21
- agentLock = 22
- agentUnlock = 23
- agentAddSmartcardKeyConstrained = 26
-
- agentConstrainLifetime = 1
- agentConstrainConfirm = 2
- agentConstrainExtension = 3
- )
- const maxAgentResponseBytes = 16 << 20
- const agentFailure = 5
- type failureAgentMsg struct{}
- const agentSuccess = 6
- type successAgentMsg struct{}
- const agentRequestIdentities = 11
- type requestIdentitiesAgentMsg struct{}
- const agentIdentitiesAnswer = 12
- type identitiesAnswerAgentMsg struct {
- NumKeys uint32 `sshtype:"12"`
- Keys []byte `ssh:"rest"`
- }
- const agentSignRequest = 13
- type signRequestAgentMsg struct {
- KeyBlob []byte `sshtype:"13"`
- Data []byte
- Flags uint32
- }
- const agentSignResponse = 14
- type signResponseAgentMsg struct {
- SigBlob []byte `sshtype:"14"`
- }
- type publicKey struct {
- Format string
- Rest []byte `ssh:"rest"`
- }
- type constrainLifetimeAgentMsg struct {
- LifetimeSecs uint32 `sshtype:"1"`
- }
- type constrainExtensionAgentMsg struct {
- ExtensionName string `sshtype:"3"`
- ExtensionDetails []byte
-
- Rest []byte `ssh:"rest"`
- }
- const agentExtension = 27
- const agentExtensionFailure = 28
- var ErrExtensionUnsupported = errors.New("agent: extension unsupported")
- type extensionAgentMsg struct {
- ExtensionType string `sshtype:"27"`
- Contents []byte
- }
- type Key struct {
- Format string
- Blob []byte
- Comment string
- }
- func clientErr(err error) error {
- return fmt.Errorf("agent: client error: %v", err)
- }
- func (k *Key) String() string {
- s := string(k.Format) + " " + base64.StdEncoding.EncodeToString(k.Blob)
- if k.Comment != "" {
- s += " " + k.Comment
- }
- return s
- }
- func (k *Key) Type() string {
- return k.Format
- }
- func (k *Key) Marshal() []byte {
- return k.Blob
- }
- func (k *Key) Verify(data []byte, sig *ssh.Signature) error {
- pubKey, err := ssh.ParsePublicKey(k.Blob)
- if err != nil {
- return fmt.Errorf("agent: bad public key: %v", err)
- }
- return pubKey.Verify(data, sig)
- }
- type wireKey struct {
- Format string
- Rest []byte `ssh:"rest"`
- }
- func parseKey(in []byte) (out *Key, rest []byte, err error) {
- var record struct {
- Blob []byte
- Comment string
- Rest []byte `ssh:"rest"`
- }
- if err := ssh.Unmarshal(in, &record); err != nil {
- return nil, nil, err
- }
- var wk wireKey
- if err := ssh.Unmarshal(record.Blob, &wk); err != nil {
- return nil, nil, err
- }
- return &Key{
- Format: wk.Format,
- Blob: record.Blob,
- Comment: record.Comment,
- }, record.Rest, nil
- }
- type client struct {
-
- conn io.ReadWriter
-
- mu sync.Mutex
- }
- func NewClient(rw io.ReadWriter) ExtendedAgent {
- return &client{conn: rw}
- }
- func (c *client) call(req []byte) (reply interface{}, err error) {
- buf, err := c.callRaw(req)
- if err != nil {
- return nil, err
- }
- reply, err = unmarshal(buf)
- if err != nil {
- return nil, clientErr(err)
- }
- return reply, nil
- }
- func (c *client) callRaw(req []byte) (reply []byte, err error) {
- c.mu.Lock()
- defer c.mu.Unlock()
- msg := make([]byte, 4+len(req))
- binary.BigEndian.PutUint32(msg, uint32(len(req)))
- copy(msg[4:], req)
- if _, err = c.conn.Write(msg); err != nil {
- return nil, clientErr(err)
- }
- var respSizeBuf [4]byte
- if _, err = io.ReadFull(c.conn, respSizeBuf[:]); err != nil {
- return nil, clientErr(err)
- }
- respSize := binary.BigEndian.Uint32(respSizeBuf[:])
- if respSize > maxAgentResponseBytes {
- return nil, clientErr(errors.New("response too large"))
- }
- buf := make([]byte, respSize)
- if _, err = io.ReadFull(c.conn, buf); err != nil {
- return nil, clientErr(err)
- }
- return buf, nil
- }
- func (c *client) simpleCall(req []byte) error {
- resp, err := c.call(req)
- if err != nil {
- return err
- }
- if _, ok := resp.(*successAgentMsg); ok {
- return nil
- }
- return errors.New("agent: failure")
- }
- func (c *client) RemoveAll() error {
- return c.simpleCall([]byte{agentRemoveAllIdentities})
- }
- func (c *client) Remove(key ssh.PublicKey) error {
- req := ssh.Marshal(&agentRemoveIdentityMsg{
- KeyBlob: key.Marshal(),
- })
- return c.simpleCall(req)
- }
- func (c *client) Lock(passphrase []byte) error {
- req := ssh.Marshal(&agentLockMsg{
- Passphrase: passphrase,
- })
- return c.simpleCall(req)
- }
- func (c *client) Unlock(passphrase []byte) error {
- req := ssh.Marshal(&agentUnlockMsg{
- Passphrase: passphrase,
- })
- return c.simpleCall(req)
- }
- func (c *client) List() ([]*Key, error) {
-
- req := []byte{agentRequestIdentities}
- msg, err := c.call(req)
- if err != nil {
- return nil, err
- }
- switch msg := msg.(type) {
- case *identitiesAnswerAgentMsg:
- if msg.NumKeys > maxAgentResponseBytes/8 {
- return nil, errors.New("agent: too many keys in agent reply")
- }
- keys := make([]*Key, msg.NumKeys)
- data := msg.Keys
- for i := uint32(0); i < msg.NumKeys; i++ {
- var key *Key
- var err error
- if key, data, err = parseKey(data); err != nil {
- return nil, err
- }
- keys[i] = key
- }
- return keys, nil
- case *failureAgentMsg:
- return nil, errors.New("agent: failed to list keys")
- }
- panic("unreachable")
- }
- func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
- return c.SignWithFlags(key, data, 0)
- }
- func (c *client) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) {
- req := ssh.Marshal(signRequestAgentMsg{
- KeyBlob: key.Marshal(),
- Data: data,
- Flags: uint32(flags),
- })
- msg, err := c.call(req)
- if err != nil {
- return nil, err
- }
- switch msg := msg.(type) {
- case *signResponseAgentMsg:
- var sig ssh.Signature
- if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil {
- return nil, err
- }
- return &sig, nil
- case *failureAgentMsg:
- return nil, errors.New("agent: failed to sign challenge")
- }
- panic("unreachable")
- }
- func unmarshal(packet []byte) (interface{}, error) {
- if len(packet) < 1 {
- return nil, errors.New("agent: empty packet")
- }
- var msg interface{}
- switch packet[0] {
- case agentFailure:
- return new(failureAgentMsg), nil
- case agentSuccess:
- return new(successAgentMsg), nil
- case agentIdentitiesAnswer:
- msg = new(identitiesAnswerAgentMsg)
- case agentSignResponse:
- msg = new(signResponseAgentMsg)
- case agentV1IdentitiesAnswer:
- msg = new(agentV1IdentityMsg)
- default:
- return nil, fmt.Errorf("agent: unknown type tag %d", packet[0])
- }
- if err := ssh.Unmarshal(packet, msg); err != nil {
- return nil, err
- }
- return msg, nil
- }
- type rsaKeyMsg struct {
- Type string `sshtype:"17|25"`
- N *big.Int
- E *big.Int
- D *big.Int
- Iqmp *big.Int
- P *big.Int
- Q *big.Int
- Comments string
- Constraints []byte `ssh:"rest"`
- }
- type dsaKeyMsg struct {
- Type string `sshtype:"17|25"`
- P *big.Int
- Q *big.Int
- G *big.Int
- Y *big.Int
- X *big.Int
- Comments string
- Constraints []byte `ssh:"rest"`
- }
- type ecdsaKeyMsg struct {
- Type string `sshtype:"17|25"`
- Curve string
- KeyBytes []byte
- D *big.Int
- Comments string
- Constraints []byte `ssh:"rest"`
- }
- type ed25519KeyMsg struct {
- Type string `sshtype:"17|25"`
- Pub []byte
- Priv []byte
- Comments string
- Constraints []byte `ssh:"rest"`
- }
- func (c *client) insertKey(s interface{}, comment string, constraints []byte) error {
- var req []byte
- switch k := s.(type) {
- case *rsa.PrivateKey:
- if len(k.Primes) != 2 {
- return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
- }
- k.Precompute()
- req = ssh.Marshal(rsaKeyMsg{
- Type: ssh.KeyAlgoRSA,
- N: k.N,
- E: big.NewInt(int64(k.E)),
- D: k.D,
- Iqmp: k.Precomputed.Qinv,
- P: k.Primes[0],
- Q: k.Primes[1],
- Comments: comment,
- Constraints: constraints,
- })
- case *dsa.PrivateKey:
- req = ssh.Marshal(dsaKeyMsg{
- Type: ssh.KeyAlgoDSA,
- P: k.P,
- Q: k.Q,
- G: k.G,
- Y: k.Y,
- X: k.X,
- Comments: comment,
- Constraints: constraints,
- })
- case *ecdsa.PrivateKey:
- nistID := fmt.Sprintf("nistp%d", k.Params().BitSize)
- req = ssh.Marshal(ecdsaKeyMsg{
- Type: "ecdsa-sha2-" + nistID,
- Curve: nistID,
- KeyBytes: elliptic.Marshal(k.Curve, k.X, k.Y),
- D: k.D,
- Comments: comment,
- Constraints: constraints,
- })
- case *ed25519.PrivateKey:
- req = ssh.Marshal(ed25519KeyMsg{
- Type: ssh.KeyAlgoED25519,
- Pub: []byte(*k)[32:],
- Priv: []byte(*k),
- Comments: comment,
- Constraints: constraints,
- })
- default:
- return fmt.Errorf("agent: unsupported key type %T", s)
- }
-
- if len(constraints) != 0 {
- req[0] = agentAddIDConstrained
- }
- resp, err := c.call(req)
- if err != nil {
- return err
- }
- if _, ok := resp.(*successAgentMsg); ok {
- return nil
- }
- return errors.New("agent: failure")
- }
- type rsaCertMsg struct {
- Type string `sshtype:"17|25"`
- CertBytes []byte
- D *big.Int
- Iqmp *big.Int
- P *big.Int
- Q *big.Int
- Comments string
- Constraints []byte `ssh:"rest"`
- }
- type dsaCertMsg struct {
- Type string `sshtype:"17|25"`
- CertBytes []byte
- X *big.Int
- Comments string
- Constraints []byte `ssh:"rest"`
- }
- type ecdsaCertMsg struct {
- Type string `sshtype:"17|25"`
- CertBytes []byte
- D *big.Int
- Comments string
- Constraints []byte `ssh:"rest"`
- }
- type ed25519CertMsg struct {
- Type string `sshtype:"17|25"`
- CertBytes []byte
- Pub []byte
- Priv []byte
- Comments string
- Constraints []byte `ssh:"rest"`
- }
- func (c *client) Add(key AddedKey) error {
- var constraints []byte
- if secs := key.LifetimeSecs; secs != 0 {
- constraints = append(constraints, ssh.Marshal(constrainLifetimeAgentMsg{secs})...)
- }
- if key.ConfirmBeforeUse {
- constraints = append(constraints, agentConstrainConfirm)
- }
- cert := key.Certificate
- if cert == nil {
- return c.insertKey(key.PrivateKey, key.Comment, constraints)
- }
- return c.insertCert(key.PrivateKey, cert, key.Comment, constraints)
- }
- func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error {
- var req []byte
- switch k := s.(type) {
- case *rsa.PrivateKey:
- if len(k.Primes) != 2 {
- return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
- }
- k.Precompute()
- req = ssh.Marshal(rsaCertMsg{
- Type: cert.Type(),
- CertBytes: cert.Marshal(),
- D: k.D,
- Iqmp: k.Precomputed.Qinv,
- P: k.Primes[0],
- Q: k.Primes[1],
- Comments: comment,
- Constraints: constraints,
- })
- case *dsa.PrivateKey:
- req = ssh.Marshal(dsaCertMsg{
- Type: cert.Type(),
- CertBytes: cert.Marshal(),
- X: k.X,
- Comments: comment,
- Constraints: constraints,
- })
- case *ecdsa.PrivateKey:
- req = ssh.Marshal(ecdsaCertMsg{
- Type: cert.Type(),
- CertBytes: cert.Marshal(),
- D: k.D,
- Comments: comment,
- Constraints: constraints,
- })
- case *ed25519.PrivateKey:
- req = ssh.Marshal(ed25519CertMsg{
- Type: cert.Type(),
- CertBytes: cert.Marshal(),
- Pub: []byte(*k)[32:],
- Priv: []byte(*k),
- Comments: comment,
- Constraints: constraints,
- })
- default:
- return fmt.Errorf("agent: unsupported key type %T", s)
- }
-
- if len(constraints) != 0 {
- req[0] = agentAddIDConstrained
- }
- signer, err := ssh.NewSignerFromKey(s)
- if err != nil {
- return err
- }
- if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 {
- return errors.New("agent: signer and cert have different public key")
- }
- resp, err := c.call(req)
- if err != nil {
- return err
- }
- if _, ok := resp.(*successAgentMsg); ok {
- return nil
- }
- return errors.New("agent: failure")
- }
- func (c *client) Signers() ([]ssh.Signer, error) {
- keys, err := c.List()
- if err != nil {
- return nil, err
- }
- var result []ssh.Signer
- for _, k := range keys {
- result = append(result, &agentKeyringSigner{c, k})
- }
- return result, nil
- }
- type agentKeyringSigner struct {
- agent *client
- pub ssh.PublicKey
- }
- func (s *agentKeyringSigner) PublicKey() ssh.PublicKey {
- return s.pub
- }
- func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) {
-
- return s.agent.Sign(s.pub, data)
- }
- func (s *agentKeyringSigner) SignWithOpts(rand io.Reader, data []byte, opts crypto.SignerOpts) (*ssh.Signature, error) {
- var flags SignatureFlags
- if opts != nil {
- switch opts.HashFunc() {
- case crypto.SHA256:
- flags = SignatureFlagRsaSha256
- case crypto.SHA512:
- flags = SignatureFlagRsaSha512
- }
- }
- return s.agent.SignWithFlags(s.pub, data, flags)
- }
- func (c *client) Extension(extensionType string, contents []byte) ([]byte, error) {
- req := ssh.Marshal(extensionAgentMsg{
- ExtensionType: extensionType,
- Contents: contents,
- })
- buf, err := c.callRaw(req)
- if err != nil {
- return nil, err
- }
- if len(buf) == 0 {
- return nil, errors.New("agent: failure; empty response")
- }
-
-
- if buf[0] == agentFailure {
- return nil, ErrExtensionUnsupported
- }
- if buf[0] == agentExtensionFailure {
- return nil, errors.New("agent: generic extension failure")
- }
- return buf, nil
- }
|