|
@@ -5,8 +5,12 @@
|
|
|
package agent
|
|
package agent
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
|
+ "crypto/dsa"
|
|
|
|
|
+ "crypto/ecdsa"
|
|
|
|
|
+ "crypto/elliptic"
|
|
|
"crypto/rsa"
|
|
"crypto/rsa"
|
|
|
"encoding/binary"
|
|
"encoding/binary"
|
|
|
|
|
+ "errors"
|
|
|
"fmt"
|
|
"fmt"
|
|
|
"io"
|
|
"io"
|
|
|
"log"
|
|
"log"
|
|
@@ -128,6 +132,7 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
return &signResponseAgentMsg{SigBlob: ssh.Marshal(sig)}, nil
|
|
return &signResponseAgentMsg{SigBlob: ssh.Marshal(sig)}, nil
|
|
|
|
|
+
|
|
|
case agentRequestIdentities:
|
|
case agentRequestIdentities:
|
|
|
keys, err := s.agent.List()
|
|
keys, err := s.agent.List()
|
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -141,42 +146,241 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
|
|
|
rep.Keys = append(rep.Keys, marshalKey(k)...)
|
|
rep.Keys = append(rep.Keys, marshalKey(k)...)
|
|
|
}
|
|
}
|
|
|
return rep, nil
|
|
return rep, nil
|
|
|
- case agentAddIdentity:
|
|
|
|
|
|
|
+
|
|
|
|
|
+ case agentAddIdConstrained, agentAddIdentity:
|
|
|
return nil, s.insertIdentity(data)
|
|
return nil, s.insertIdentity(data)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("unknown opcode %d", data[0])
|
|
return nil, fmt.Errorf("unknown opcode %d", data[0])
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func parseRSAKey(req []byte) (*AddedKey, error) {
|
|
|
|
|
+ var k rsaKeyMsg
|
|
|
|
|
+ if err := ssh.Unmarshal(req, &k); err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+ if k.E.BitLen() > 30 {
|
|
|
|
|
+ return nil, errors.New("agent: RSA public exponent too large")
|
|
|
|
|
+ }
|
|
|
|
|
+ priv := &rsa.PrivateKey{
|
|
|
|
|
+ PublicKey: rsa.PublicKey{
|
|
|
|
|
+ E: int(k.E.Int64()),
|
|
|
|
|
+ N: k.N,
|
|
|
|
|
+ },
|
|
|
|
|
+ D: k.D,
|
|
|
|
|
+ Primes: []*big.Int{k.P, k.Q},
|
|
|
|
|
+ }
|
|
|
|
|
+ priv.Precompute()
|
|
|
|
|
+
|
|
|
|
|
+ return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func parseDSAKey(req []byte) (*AddedKey, error) {
|
|
|
|
|
+ var k dsaKeyMsg
|
|
|
|
|
+ if err := ssh.Unmarshal(req, &k); err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+ priv := &dsa.PrivateKey{
|
|
|
|
|
+ PublicKey: dsa.PublicKey{
|
|
|
|
|
+ Parameters: dsa.Parameters{
|
|
|
|
|
+ P: k.P,
|
|
|
|
|
+ Q: k.Q,
|
|
|
|
|
+ G: k.G,
|
|
|
|
|
+ },
|
|
|
|
|
+ Y: k.Y,
|
|
|
|
|
+ },
|
|
|
|
|
+ X: k.X,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func unmarshalECDSA(curveName string, keyBytes []byte, privScalar *big.Int) (priv *ecdsa.PrivateKey, err error) {
|
|
|
|
|
+ priv = &ecdsa.PrivateKey{
|
|
|
|
|
+ D: privScalar,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ switch curveName {
|
|
|
|
|
+ case "nistp256":
|
|
|
|
|
+ priv.Curve = elliptic.P256()
|
|
|
|
|
+ case "nistp384":
|
|
|
|
|
+ priv.Curve = elliptic.P384()
|
|
|
|
|
+ case "nistp521":
|
|
|
|
|
+ priv.Curve = elliptic.P521()
|
|
|
|
|
+ default:
|
|
|
|
|
+ return nil, fmt.Errorf("agent: unknown curve %q", curveName)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ priv.X, priv.Y = elliptic.Unmarshal(priv.Curve, keyBytes)
|
|
|
|
|
+ if priv.X == nil || priv.Y == nil {
|
|
|
|
|
+ return nil, errors.New("agent: point not on curve")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return priv, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func parseECDSAKey(req []byte) (*AddedKey, error) {
|
|
|
|
|
+ var k ecdsaKeyMsg
|
|
|
|
|
+ if err := ssh.Unmarshal(req, &k); err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ priv, err := unmarshalECDSA(k.Curve, k.KeyBytes, k.D)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return &AddedKey{PrivateKey: &priv, Comment: k.Comments}, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func parseRSACert(req []byte) (*AddedKey, error) {
|
|
|
|
|
+ var k rsaCertMsg
|
|
|
|
|
+ if err := ssh.Unmarshal(req, &k); err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pubKey, err := ssh.ParsePublicKey(k.CertBytes)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ cert, ok := pubKey.(*ssh.Certificate)
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ return nil, errors.New("agent: bad RSA certificate")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // An RSA publickey as marshaled by rsaPublicKey.Marshal() in keys.go
|
|
|
|
|
+ var rsaPub struct {
|
|
|
|
|
+ Name string
|
|
|
|
|
+ E *big.Int
|
|
|
|
|
+ N *big.Int
|
|
|
|
|
+ }
|
|
|
|
|
+ if err := ssh.Unmarshal(cert.Key.Marshal(), &rsaPub); err != nil {
|
|
|
|
|
+ return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if rsaPub.E.BitLen() > 30 {
|
|
|
|
|
+ return nil, errors.New("agent: RSA public exponent too large")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ priv := rsa.PrivateKey{
|
|
|
|
|
+ PublicKey: rsa.PublicKey{
|
|
|
|
|
+ E: int(rsaPub.E.Int64()),
|
|
|
|
|
+ N: rsaPub.N,
|
|
|
|
|
+ },
|
|
|
|
|
+ D: k.D,
|
|
|
|
|
+ Primes: []*big.Int{k.Q, k.P},
|
|
|
|
|
+ }
|
|
|
|
|
+ priv.Precompute()
|
|
|
|
|
+
|
|
|
|
|
+ return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func parseDSACert(req []byte) (*AddedKey, error) {
|
|
|
|
|
+ var k dsaCertMsg
|
|
|
|
|
+ if err := ssh.Unmarshal(req, &k); err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+ pubKey, err := ssh.ParsePublicKey(k.CertBytes)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+ cert, ok := pubKey.(*ssh.Certificate)
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ return nil, errors.New("agent: bad DSA certificate")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // A DSA publickey as marshaled by dsaPublicKey.Marshal() in keys.go
|
|
|
|
|
+ var w struct {
|
|
|
|
|
+ Name string
|
|
|
|
|
+ P, Q, G, Y *big.Int
|
|
|
|
|
+ }
|
|
|
|
|
+ if err := ssh.Unmarshal(cert.Key.Marshal(), &w); err != nil {
|
|
|
|
|
+ return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ priv := &dsa.PrivateKey{
|
|
|
|
|
+ PublicKey: dsa.PublicKey{
|
|
|
|
|
+ Parameters: dsa.Parameters{
|
|
|
|
|
+ P: w.P,
|
|
|
|
|
+ Q: w.Q,
|
|
|
|
|
+ G: w.G,
|
|
|
|
|
+ },
|
|
|
|
|
+ Y: w.Y,
|
|
|
|
|
+ },
|
|
|
|
|
+ X: k.X,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func parseECDSACert(req []byte) (*AddedKey, error) {
|
|
|
|
|
+ var k ecdsaCertMsg
|
|
|
|
|
+ if err := ssh.Unmarshal(req, &k); err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pubKey, err := ssh.ParsePublicKey(k.CertBytes)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+ cert, ok := pubKey.(*ssh.Certificate)
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ return nil, errors.New("agent: bad ECDSA certificate")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // An ECDSA publickey as marshaled by ecdsaPublicKey.Marshal() in keys.go
|
|
|
|
|
+ var ecdsaPub struct {
|
|
|
|
|
+ Name string
|
|
|
|
|
+ ID string
|
|
|
|
|
+ Key []byte
|
|
|
|
|
+ }
|
|
|
|
|
+ if err := ssh.Unmarshal(cert.Key.Marshal(), &ecdsaPub); err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ priv, err := unmarshalECDSA(ecdsaPub.ID, ecdsaPub.Key, k.D)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func (s *server) insertIdentity(req []byte) error {
|
|
func (s *server) insertIdentity(req []byte) error {
|
|
|
var record struct {
|
|
var record struct {
|
|
|
- Type string `sshtype:"17"`
|
|
|
|
|
|
|
+ Type string `sshtype:"17|25"`
|
|
|
Rest []byte `ssh:"rest"`
|
|
Rest []byte `ssh:"rest"`
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
if err := ssh.Unmarshal(req, &record); err != nil {
|
|
if err := ssh.Unmarshal(req, &record); err != nil {
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ var addedKey *AddedKey
|
|
|
|
|
+ var err error
|
|
|
|
|
+
|
|
|
switch record.Type {
|
|
switch record.Type {
|
|
|
case ssh.KeyAlgoRSA:
|
|
case ssh.KeyAlgoRSA:
|
|
|
- var k rsaKeyMsg
|
|
|
|
|
- if err := ssh.Unmarshal(req, &k); err != nil {
|
|
|
|
|
- return err
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- priv := rsa.PrivateKey{
|
|
|
|
|
- PublicKey: rsa.PublicKey{
|
|
|
|
|
- E: int(k.E.Int64()),
|
|
|
|
|
- N: k.N,
|
|
|
|
|
- },
|
|
|
|
|
- D: k.D,
|
|
|
|
|
- Primes: []*big.Int{k.P, k.Q},
|
|
|
|
|
- }
|
|
|
|
|
- priv.Precompute()
|
|
|
|
|
|
|
+ addedKey, err = parseRSAKey(req)
|
|
|
|
|
+ case ssh.KeyAlgoDSA:
|
|
|
|
|
+ addedKey, err = parseDSAKey(req)
|
|
|
|
|
+ case ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521:
|
|
|
|
|
+ addedKey, err = parseECDSACert(req)
|
|
|
|
|
+ case ssh.CertAlgoRSAv01:
|
|
|
|
|
+ addedKey, err = parseRSACert(req)
|
|
|
|
|
+ case ssh.CertAlgoDSAv01:
|
|
|
|
|
+ addedKey, err = parseDSACert(req)
|
|
|
|
|
+ case ssh.CertAlgoECDSA256v01, ssh.CertAlgoECDSA384v01, ssh.CertAlgoECDSA521v01:
|
|
|
|
|
+ addedKey, err = parseECDSACert(req)
|
|
|
|
|
+ default:
|
|
|
|
|
+ return fmt.Errorf("agent: not implemented: %q", record.Type)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return s.agent.Add(AddedKey{PrivateKey: &priv, Comment: k.Comments})
|
|
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
- return fmt.Errorf("not implemented: %s", record.Type)
|
|
|
|
|
|
|
+ return s.agent.Add(*addedKey)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ServeAgent serves the agent protocol on the given connection. It
|
|
// ServeAgent serves the agent protocol on the given connection. It
|