go.crypto/ssh: Add support for ECDSA keys and certs.
R=agl, dave CC=golang-dev https://golang.org/cl/6873060
This commit is contained in:
committed by
Dave Cheney
parent
4002be2701
commit
94c9f92c92
+21
-9
@@ -9,17 +9,18 @@ package ssh
|
||||
|
||||
import (
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"time"
|
||||
)
|
||||
|
||||
// String constants in [PROTOCOL.certkeys] for certificate algorithm names.
|
||||
const (
|
||||
hostAlgoRSACertV01 = "ssh-rsa-cert-v01@openssh.com"
|
||||
hostAlgoDSACertV01 = "ssh-dss-cert-v01@openssh.com"
|
||||
hostAlgoECDSA256CertV01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
|
||||
hostAlgoECDSA384CertV01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
|
||||
hostAlgoECDSA521CertV01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
|
||||
certAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
|
||||
certAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
|
||||
certAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
|
||||
certAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
|
||||
certAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
|
||||
)
|
||||
|
||||
// Certificate types are used to specify whether a certificate is for identification
|
||||
@@ -41,10 +42,12 @@ type tuple struct {
|
||||
|
||||
// An OpenSSHCertV01 represents an OpenSSH certificate as defined in
|
||||
// [PROTOCOL.certkeys] rev 1.8. Supported formats include
|
||||
// ssh-rsa-cert-v01@openssh.com and ssh-dss-cert-v01@openssh.com.
|
||||
// ssh-rsa-cert-v01@openssh.com, ssh-dss-cert-v01@openssh.com,
|
||||
// ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com,
|
||||
// and ecdsa-sha2-nistp521-cert-v01@openssh.com.
|
||||
type OpenSSHCertV01 struct {
|
||||
Nonce []byte
|
||||
Key interface{} // rsa or dsa *PublicKey
|
||||
Key interface{} // rsa, dsa, or ecdsa *PublicKey
|
||||
Serial uint64
|
||||
Type uint32
|
||||
KeyId string
|
||||
@@ -65,18 +68,24 @@ func parseOpenSSHCertV01(in []byte, algo string) (out *OpenSSHCertV01, rest []by
|
||||
}
|
||||
|
||||
switch algo {
|
||||
case hostAlgoRSACertV01:
|
||||
case certAlgoRSAv01:
|
||||
var rsaPubKey *rsa.PublicKey
|
||||
if rsaPubKey, in, ok = parseRSA(in); !ok {
|
||||
return
|
||||
}
|
||||
cert.Key = rsaPubKey
|
||||
case hostAlgoDSACertV01:
|
||||
case certAlgoDSAv01:
|
||||
var dsaPubKey *dsa.PublicKey
|
||||
if dsaPubKey, in, ok = parseDSA(in); !ok {
|
||||
return
|
||||
}
|
||||
cert.Key = dsaPubKey
|
||||
case certAlgoECDSA256v01, certAlgoECDSA384v01, certAlgoECDSA521v01:
|
||||
var ecdsaPubKey *ecdsa.PublicKey
|
||||
if ecdsaPubKey, in, ok = parseECDSA(in); !ok {
|
||||
return
|
||||
}
|
||||
cert.Key = ecdsaPubKey
|
||||
default:
|
||||
ok = false
|
||||
return
|
||||
@@ -149,6 +158,9 @@ func marshalOpenSSHCertV01(cert *OpenSSHCertV01) []byte {
|
||||
case *dsa.PublicKey:
|
||||
k := cert.Key.(*dsa.PublicKey)
|
||||
pubKey = marshalPubDSA(k)
|
||||
case *ecdsa.PublicKey:
|
||||
k := cert.Key.(*ecdsa.PublicKey)
|
||||
pubKey = marshalPubECDSA(k)
|
||||
default:
|
||||
panic("ssh: unknown public key type in cert")
|
||||
}
|
||||
|
||||
+21
-3
@@ -6,6 +6,7 @@ package ssh
|
||||
|
||||
import (
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -191,11 +192,17 @@ func serializeSignature(algoname string, sig []byte) []byte {
|
||||
switch algoname {
|
||||
// The corresponding private key to a public certificate is always a normal
|
||||
// private key. For signature serialization purposes, ensure we use the
|
||||
// proper ssh-rsa or ssh-dss algo name in case the public cert algo name is passed.
|
||||
case hostAlgoRSACertV01:
|
||||
// proper key algorithm name in case the public cert algorithm name is passed.
|
||||
case certAlgoRSAv01:
|
||||
algoname = "ssh-rsa"
|
||||
case hostAlgoDSACertV01:
|
||||
case certAlgoDSAv01:
|
||||
algoname = "ssh-dss"
|
||||
case certAlgoECDSA256v01:
|
||||
algoname = "ecdsa-sha2-nistp256"
|
||||
case certAlgoECDSA384v01:
|
||||
algoname = "ecdsa-sha2-nistp384"
|
||||
case certAlgoECDSA521v01:
|
||||
algoname = "ecdsa-sha2-nistp521"
|
||||
}
|
||||
length := stringLength(len(algoname))
|
||||
length += stringLength(len(sig))
|
||||
@@ -216,6 +223,8 @@ func serializePublickey(key interface{}) []byte {
|
||||
pubKeyBytes = marshalPubRSA(key)
|
||||
case *dsa.PublicKey:
|
||||
pubKeyBytes = marshalPubDSA(key)
|
||||
case *ecdsa.PublicKey:
|
||||
pubKeyBytes = marshalPubECDSA(key)
|
||||
case *OpenSSHCertV01:
|
||||
pubKeyBytes = marshalOpenSSHCertV01(key)
|
||||
default:
|
||||
@@ -236,6 +245,15 @@ func algoName(key interface{}) string {
|
||||
return "ssh-rsa"
|
||||
case *dsa.PublicKey:
|
||||
return "ssh-dss"
|
||||
case *ecdsa.PublicKey:
|
||||
switch key.(*ecdsa.PublicKey).Params().BitSize {
|
||||
case 256:
|
||||
return "ecdsa-sha2-nistp256"
|
||||
case 384:
|
||||
return "ecdsa-sha2-nistp384"
|
||||
case 521:
|
||||
return "ecdsa-sha2-nistp521"
|
||||
}
|
||||
case *OpenSSHCertV01:
|
||||
return algoName(key.(*OpenSSHCertV01).Key) + "-cert-v01@openssh.com"
|
||||
}
|
||||
|
||||
+96
-12
@@ -7,6 +7,8 @@ package ssh
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"encoding/base64"
|
||||
"math/big"
|
||||
@@ -29,11 +31,13 @@ func parsePubKey(in []byte) (out interface{}, rest []byte, ok bool) {
|
||||
}
|
||||
|
||||
switch string(algo) {
|
||||
case hostAlgoRSA:
|
||||
case keyAlgoRSA:
|
||||
return parseRSA(in)
|
||||
case hostAlgoDSA:
|
||||
case keyAlgoDSA:
|
||||
return parseDSA(in)
|
||||
case hostAlgoRSACertV01, hostAlgoDSACertV01:
|
||||
case keyAlgoECDSA256, keyAlgoECDSA384, keyAlgoECDSA521:
|
||||
return parseECDSA(in)
|
||||
case certAlgoRSAv01, certAlgoDSAv01, certAlgoECDSA256v01, certAlgoECDSA384v01, certAlgoECDSA521v01:
|
||||
return parseOpenSSHCertV01(in, string(algo))
|
||||
}
|
||||
panic("ssh: unknown public key type")
|
||||
@@ -86,15 +90,49 @@ func parseDSA(in []byte) (out *dsa.PublicKey, rest []byte, ok bool) {
|
||||
return key, in, ok
|
||||
}
|
||||
|
||||
// parseECDSA parses an ECDSA key according to RFC 5656, section 3.1.
|
||||
func parseECDSA(in []byte) (out *ecdsa.PublicKey, rest []byte, ok bool) {
|
||||
var identifier []byte
|
||||
if identifier, in, ok = parseString(in); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
key := new(ecdsa.PublicKey)
|
||||
|
||||
switch string(identifier) {
|
||||
case "nistp256":
|
||||
key.Curve = elliptic.P256()
|
||||
case "nistp384":
|
||||
key.Curve = elliptic.P384()
|
||||
case "nistp521":
|
||||
key.Curve = elliptic.P521()
|
||||
default:
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
|
||||
var keyBytes []byte
|
||||
if keyBytes, in, ok = parseString(in); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
key.X, key.Y = elliptic.Unmarshal(key.Curve, keyBytes)
|
||||
if key.X == nil || key.Y == nil {
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
return key, in, ok
|
||||
}
|
||||
|
||||
// marshalPrivRSA serializes an RSA private key according to RFC 4253, section 6.6.
|
||||
func marshalPrivRSA(priv *rsa.PrivateKey) []byte {
|
||||
e := new(big.Int).SetInt64(int64(priv.E))
|
||||
length := stringLength(len(hostAlgoRSA))
|
||||
length := stringLength(len(keyAlgoRSA))
|
||||
length += intLength(e)
|
||||
length += intLength(priv.N)
|
||||
|
||||
ret := make([]byte, length)
|
||||
r := marshalString(ret, []byte(hostAlgoRSA))
|
||||
r := marshalString(ret, []byte(keyAlgoRSA))
|
||||
r = marshalInt(r, e)
|
||||
r = marshalInt(r, priv.N)
|
||||
|
||||
@@ -125,11 +163,35 @@ func marshalPubDSA(key *dsa.PublicKey) []byte {
|
||||
r := marshalInt(ret, key.P)
|
||||
r = marshalInt(r, key.Q)
|
||||
r = marshalInt(r, key.G)
|
||||
marshalInt(r, key.Y)
|
||||
r = marshalInt(r, key.Y)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// marshalPubECDSA serializes an ECDSA public key according to RFC 5656, section 3.1.
|
||||
func marshalPubECDSA(key *ecdsa.PublicKey) []byte {
|
||||
var identifier []byte
|
||||
switch key.Params().BitSize {
|
||||
case 256:
|
||||
identifier = []byte("nistp256")
|
||||
case 384:
|
||||
identifier = []byte("nistp384")
|
||||
case 521:
|
||||
identifier = []byte("nistp521")
|
||||
default:
|
||||
panic("ssh: unsupported ecdsa key size")
|
||||
}
|
||||
keyBytes := elliptic.Marshal(key.Curve, key.X, key.Y)
|
||||
|
||||
length := stringLength(len(identifier))
|
||||
length += stringLength(len(keyBytes))
|
||||
|
||||
ret := make([]byte, length)
|
||||
r := marshalString(ret, identifier)
|
||||
r = marshalString(r, keyBytes)
|
||||
return ret
|
||||
}
|
||||
|
||||
// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
|
||||
// (see sshd(8) manual page) once the options and key type fields have been
|
||||
// removed.
|
||||
@@ -196,8 +258,8 @@ func ParseAuthorizedKey(in []byte) (out interface{}, comment string, options []s
|
||||
// We don't support these keys.
|
||||
in = rest
|
||||
continue
|
||||
case hostAlgoRSACertV01, hostAlgoDSACertV01,
|
||||
hostAlgoECDSA256CertV01, hostAlgoECDSA384CertV01, hostAlgoECDSA521CertV01:
|
||||
case certAlgoRSAv01, certAlgoDSAv01,
|
||||
certAlgoECDSA256v01, certAlgoECDSA384v01, certAlgoECDSA521v01:
|
||||
// We don't support these certificates.
|
||||
in = rest
|
||||
continue
|
||||
@@ -270,15 +332,37 @@ func MarshalAuthorizedKey(key interface{}) []byte {
|
||||
b := &bytes.Buffer{}
|
||||
switch keyType := key.(type) {
|
||||
case *rsa.PublicKey:
|
||||
b.WriteString(hostAlgoRSA)
|
||||
b.WriteString(keyAlgoRSA)
|
||||
case *dsa.PublicKey:
|
||||
b.WriteString(hostAlgoDSA)
|
||||
b.WriteString(keyAlgoDSA)
|
||||
case *ecdsa.PublicKey:
|
||||
switch keyType.Params().BitSize {
|
||||
case 256:
|
||||
b.WriteString(keyAlgoECDSA256)
|
||||
case 384:
|
||||
b.WriteString(keyAlgoECDSA384)
|
||||
case 521:
|
||||
b.WriteString(keyAlgoECDSA521)
|
||||
default:
|
||||
panic("unexpected key type")
|
||||
}
|
||||
case *OpenSSHCertV01:
|
||||
switch keyType.Key.(type) {
|
||||
case *rsa.PublicKey:
|
||||
b.WriteString(hostAlgoRSACertV01)
|
||||
b.WriteString(certAlgoRSAv01)
|
||||
case *dsa.PublicKey:
|
||||
b.WriteString(hostAlgoDSACertV01)
|
||||
b.WriteString(certAlgoDSAv01)
|
||||
case *ecdsa.PublicKey:
|
||||
switch keyType.Key.(*ecdsa.PublicKey).Params().BitSize {
|
||||
case 256:
|
||||
b.WriteString(certAlgoECDSA256v01)
|
||||
case 384:
|
||||
b.WriteString(certAlgoECDSA384v01)
|
||||
case 521:
|
||||
b.WriteString(certAlgoECDSA521v01)
|
||||
default:
|
||||
panic("unexpected key type")
|
||||
}
|
||||
default:
|
||||
panic("unexpected key type")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user