|
|
@@ -20,6 +20,8 @@ import (
|
|
|
"io"
|
|
|
"math/big"
|
|
|
"strings"
|
|
|
+
|
|
|
+ "golang.org/x/crypto/ed25519"
|
|
|
)
|
|
|
|
|
|
// These constants represent the algorithm names for key types supported by this
|
|
|
@@ -30,6 +32,7 @@ const (
|
|
|
KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
|
|
|
KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
|
|
|
KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
|
|
|
+ KeyAlgoED25519 = "ssh-ed25519"
|
|
|
)
|
|
|
|
|
|
// parsePubKey parses a public key of the given algorithm.
|
|
|
@@ -42,14 +45,16 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err
|
|
|
return parseDSA(in)
|
|
|
case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
|
|
|
return parseECDSA(in)
|
|
|
- case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
|
|
|
+ case KeyAlgoED25519:
|
|
|
+ return parseED25519(in)
|
|
|
+ case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01:
|
|
|
cert, err := parseCert(in, certToPrivAlgo(algo))
|
|
|
if err != nil {
|
|
|
return nil, nil, err
|
|
|
}
|
|
|
return cert, nil, nil
|
|
|
}
|
|
|
- return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", err)
|
|
|
+ return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", algo)
|
|
|
}
|
|
|
|
|
|
// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
|
|
|
@@ -459,6 +464,51 @@ func (key *ecdsaPublicKey) nistID() string {
|
|
|
panic("ssh: unsupported ecdsa key size")
|
|
|
}
|
|
|
|
|
|
+type ed25519PublicKey ed25519.PublicKey
|
|
|
+
|
|
|
+func (key ed25519PublicKey) Type() string {
|
|
|
+ return KeyAlgoED25519
|
|
|
+}
|
|
|
+
|
|
|
+func parseED25519(in []byte) (out PublicKey, rest []byte, err error) {
|
|
|
+ var w struct {
|
|
|
+ KeyBytes []byte
|
|
|
+ Rest []byte `ssh:"rest"`
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := Unmarshal(in, &w); err != nil {
|
|
|
+ return nil, nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ key := ed25519.PublicKey(w.KeyBytes)
|
|
|
+
|
|
|
+ return (ed25519PublicKey)(key), w.Rest, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (key ed25519PublicKey) Marshal() []byte {
|
|
|
+ w := struct {
|
|
|
+ Name string
|
|
|
+ KeyBytes []byte
|
|
|
+ }{
|
|
|
+ KeyAlgoED25519,
|
|
|
+ []byte(key),
|
|
|
+ }
|
|
|
+ return Marshal(&w)
|
|
|
+}
|
|
|
+
|
|
|
+func (key ed25519PublicKey) Verify(b []byte, sig *Signature) error {
|
|
|
+ if sig.Format != key.Type() {
|
|
|
+ return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type())
|
|
|
+ }
|
|
|
+
|
|
|
+ edKey := (ed25519.PublicKey)(key)
|
|
|
+ if ok := ed25519.Verify(edKey, b, sig.Blob); !ok {
|
|
|
+ return errors.New("ssh: signature did not verify")
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
func supportedEllipticCurve(curve elliptic.Curve) bool {
|
|
|
return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521()
|
|
|
}
|
|
|
@@ -597,13 +647,19 @@ func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
|
|
hashFunc = crypto.SHA1
|
|
|
case *ecdsaPublicKey:
|
|
|
hashFunc = ecHash(key.Curve)
|
|
|
+ case ed25519PublicKey:
|
|
|
default:
|
|
|
return nil, fmt.Errorf("ssh: unsupported key type %T", key)
|
|
|
}
|
|
|
|
|
|
- h := hashFunc.New()
|
|
|
- h.Write(data)
|
|
|
- digest := h.Sum(nil)
|
|
|
+ var digest []byte
|
|
|
+ if hashFunc != 0 {
|
|
|
+ h := hashFunc.New()
|
|
|
+ h.Write(data)
|
|
|
+ digest = h.Sum(nil)
|
|
|
+ } else {
|
|
|
+ digest = data
|
|
|
+ }
|
|
|
|
|
|
signature, err := s.signer.Sign(rand, digest, hashFunc)
|
|
|
if err != nil {
|
|
|
@@ -643,9 +699,9 @@ func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
-// NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or
|
|
|
-// any other crypto.Signer and returns a corresponding Signer instance. ECDSA
|
|
|
-// keys must use P-256, P-384 or P-521.
|
|
|
+// NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey,
|
|
|
+// ed25519.PublicKey, or any other crypto.Signer and returns a corresponding
|
|
|
+// Signer instance. ECDSA keys must use P-256, P-384 or P-521.
|
|
|
func NewPublicKey(key interface{}) (PublicKey, error) {
|
|
|
switch key := key.(type) {
|
|
|
case *rsa.PublicKey:
|
|
|
@@ -657,6 +713,8 @@ func NewPublicKey(key interface{}) (PublicKey, error) {
|
|
|
return (*ecdsaPublicKey)(key), nil
|
|
|
case *dsa.PublicKey:
|
|
|
return (*dsaPublicKey)(key), nil
|
|
|
+ case ed25519.PublicKey:
|
|
|
+ return (ed25519PublicKey)(key), nil
|
|
|
default:
|
|
|
return nil, fmt.Errorf("ssh: unsupported key type %T", key)
|
|
|
}
|
|
|
@@ -688,6 +746,8 @@ func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
|
|
|
return x509.ParseECPrivateKey(block.Bytes)
|
|
|
case "DSA PRIVATE KEY":
|
|
|
return ParseDSAPrivateKey(block.Bytes)
|
|
|
+ case "OPENSSH PRIVATE KEY":
|
|
|
+ return parseOpenSSHPrivateKey(block.Bytes)
|
|
|
default:
|
|
|
return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
|
|
|
}
|
|
|
@@ -724,3 +784,63 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
|
|
|
X: k.Pub,
|
|
|
}, nil
|
|
|
}
|
|
|
+
|
|
|
+// Implemented based on the documentation at
|
|
|
+// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
|
|
|
+func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) {
|
|
|
+ magic := append([]byte("openssh-key-v1"), 0)
|
|
|
+ if !bytes.Equal(magic, key[0:len(magic)]) {
|
|
|
+ return nil, errors.New("ssh: invalid openssh private key format")
|
|
|
+ }
|
|
|
+ remaining := key[len(magic):]
|
|
|
+
|
|
|
+ var w struct {
|
|
|
+ CipherName string
|
|
|
+ KdfName string
|
|
|
+ KdfOpts string
|
|
|
+ NumKeys uint32
|
|
|
+ PubKey []byte
|
|
|
+ PrivKeyBlock []byte
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := Unmarshal(remaining, &w); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ pk1 := struct {
|
|
|
+ Check1 uint32
|
|
|
+ Check2 uint32
|
|
|
+ Keytype string
|
|
|
+ Pub []byte
|
|
|
+ Priv []byte
|
|
|
+ Comment string
|
|
|
+ Pad []byte `ssh:"rest"`
|
|
|
+ }{}
|
|
|
+
|
|
|
+ if err := Unmarshal(w.PrivKeyBlock, &pk1); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ if pk1.Check1 != pk1.Check2 {
|
|
|
+ return nil, errors.New("ssh: checkint mismatch")
|
|
|
+ }
|
|
|
+
|
|
|
+ // we only handle ed25519 keys currently
|
|
|
+ if pk1.Keytype != KeyAlgoED25519 {
|
|
|
+ return nil, errors.New("ssh: unhandled key type")
|
|
|
+ }
|
|
|
+
|
|
|
+ for i, b := range pk1.Pad {
|
|
|
+ if int(b) != i+1 {
|
|
|
+ return nil, errors.New("ssh: padding not as expected")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(pk1.Priv) != ed25519.PrivateKeySize {
|
|
|
+ return nil, errors.New("ssh: private key unexpected length")
|
|
|
+ }
|
|
|
+
|
|
|
+ pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize))
|
|
|
+ copy(pk, pk1.Priv)
|
|
|
+ return &pk, nil
|
|
|
+}
|