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:
Jonathan Pittman
2012-12-14 05:52:19 +11:00
committed by Dave Cheney
parent 4002be2701
commit 94c9f92c92
3 changed files with 138 additions and 24 deletions
+21 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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")
}