Commit 599ec772 authored by Filippo Valsorda's avatar Filippo Valsorda

crypto/x509: add support for Ed25519 certificates and keys

Based on RFC 8410.

Updates #25355

Change-Id: If7abb7eeb0ede10a9bb3d2004f2116e587c6207a
Reviewed-on: https://go-review.googlesource.com/c/go/+/175478
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarAdam Langley <agl@golang.org>
parent ab242dcb
...@@ -6,6 +6,7 @@ package x509 ...@@ -6,6 +6,7 @@ package x509
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa" "crypto/rsa"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1" "encoding/asn1"
...@@ -23,8 +24,9 @@ type pkcs8 struct { ...@@ -23,8 +24,9 @@ type pkcs8 struct {
// optional attributes omitted. // optional attributes omitted.
} }
// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. // ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. It returns a
// See RFC 5208. // *rsa.PrivateKey, a *ecdsa.PrivateKey, or a ed25519.PrivateKey. More types
// might be supported in future versions. See RFC 5208 and RFC 8410.
func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) { func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
var privKey pkcs8 var privKey pkcs8
if _, err := asn1.Unmarshal(der, &privKey); err != nil { if _, err := asn1.Unmarshal(der, &privKey); err != nil {
...@@ -56,16 +58,28 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) { ...@@ -56,16 +58,28 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
} }
return key, nil return key, nil
case privKey.Algo.Algorithm.Equal(oidPublicKeyEd25519):
if l := len(privKey.Algo.Parameters.FullBytes); l != 0 {
return nil, errors.New("x509: invalid Ed25519 private key parameters")
}
var curvePrivateKey []byte
if _, err := asn1.Unmarshal(privKey.PrivateKey, &curvePrivateKey); err != nil {
return nil, fmt.Errorf("x509: invalid Ed25519 private key: %v", err)
}
if l := len(curvePrivateKey); l != ed25519.SeedSize {
return nil, fmt.Errorf("x509: invalid Ed25519 private key length: %d", l)
}
return ed25519.NewKeyFromSeed(curvePrivateKey), nil
default: default:
return nil, fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm) return nil, fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
} }
} }
// MarshalPKCS8PrivateKey converts a private key to PKCS#8 encoded form. // MarshalPKCS8PrivateKey converts a private key to PKCS#8 encoded form.
// The following key types are supported: *rsa.PrivateKey, *ecdsa.PrivateKey. // The following key types are currently supported: *rsa.PrivateKey,
// Unsupported key types result in an error. // *ecdsa.PrivateKey and ed25519.PrivateKey. Unsupported key types result in an
// // error. See RFC 5208 and RFC 8410.
// See RFC 5208.
func MarshalPKCS8PrivateKey(key interface{}) ([]byte, error) { func MarshalPKCS8PrivateKey(key interface{}) ([]byte, error) {
var privKey pkcs8 var privKey pkcs8
...@@ -99,6 +113,16 @@ func MarshalPKCS8PrivateKey(key interface{}) ([]byte, error) { ...@@ -99,6 +113,16 @@ func MarshalPKCS8PrivateKey(key interface{}) ([]byte, error) {
return nil, errors.New("x509: failed to marshal EC private key while building PKCS#8: " + err.Error()) return nil, errors.New("x509: failed to marshal EC private key while building PKCS#8: " + err.Error())
} }
case ed25519.PrivateKey:
privKey.Algo = pkix.AlgorithmIdentifier{
Algorithm: oidPublicKeyEd25519,
}
curvePrivateKey, err := asn1.Marshal(k.Seed())
if err != nil {
return nil, fmt.Errorf("x509: failed to marshal private key: %v", err)
}
privKey.PrivateKey = curvePrivateKey
default: default:
return nil, fmt.Errorf("x509: unknown key type while marshaling PKCS#8: %T", key) return nil, fmt.Errorf("x509: unknown key type while marshaling PKCS#8: %T", key)
} }
......
...@@ -7,6 +7,7 @@ package x509 ...@@ -7,6 +7,7 @@ package x509
import ( import (
"bytes" "bytes"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic" "crypto/elliptic"
"crypto/rsa" "crypto/rsa"
"encoding/hex" "encoding/hex"
...@@ -40,6 +41,9 @@ var pkcs8P384PrivateKeyHex = `3081b6020100301006072a8648ce3d020106052b8104002204 ...@@ -40,6 +41,9 @@ var pkcs8P384PrivateKeyHex = `3081b6020100301006072a8648ce3d020106052b8104002204
// expected and the Go test will fail to recreate it exactly. // expected and the Go test will fail to recreate it exactly.
var pkcs8P521PrivateKeyHex = `3081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044200cfe0b87113a205cf291bb9a8cd1a74ac6c7b2ebb8199aaa9a5010d8b8012276fa3c22ac913369fa61beec2a3b8b4516bc049bde4fb3b745ac11b56ab23ac52e361a1818903818600040138f75acdd03fbafa4f047a8e4b272ba9d555c667962b76f6f232911a5786a0964e5edea6bd21a6f8725720958de049c6e3e6661c1c91b227cebee916c0319ed6ca003db0a3206d372229baf9dd25d868bf81140a518114803ce40c1855074d68c4e9dab9e65efba7064c703b400f1767f217dac82715ac1f6d88c74baf47a7971de4ea` var pkcs8P521PrivateKeyHex = `3081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044200cfe0b87113a205cf291bb9a8cd1a74ac6c7b2ebb8199aaa9a5010d8b8012276fa3c22ac913369fa61beec2a3b8b4516bc049bde4fb3b745ac11b56ab23ac52e361a1818903818600040138f75acdd03fbafa4f047a8e4b272ba9d555c667962b76f6f232911a5786a0964e5edea6bd21a6f8725720958de049c6e3e6661c1c91b227cebee916c0319ed6ca003db0a3206d372229baf9dd25d868bf81140a518114803ce40c1855074d68c4e9dab9e65efba7064c703b400f1767f217dac82715ac1f6d88c74baf47a7971de4ea`
// From RFC 8410, Section 7.
var pkcs8Ed25519PrivateKeyHex = `302e020100300506032b657004220420d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842`
func TestPKCS8(t *testing.T) { func TestPKCS8(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
...@@ -76,6 +80,11 @@ func TestPKCS8(t *testing.T) { ...@@ -76,6 +80,11 @@ func TestPKCS8(t *testing.T) {
keyType: reflect.TypeOf(&ecdsa.PrivateKey{}), keyType: reflect.TypeOf(&ecdsa.PrivateKey{}),
curve: elliptic.P521(), curve: elliptic.P521(),
}, },
{
name: "Ed25519 private key",
keyHex: pkcs8Ed25519PrivateKeyHex,
keyType: reflect.TypeOf(ed25519.PrivateKey{}),
},
} }
for _, test := range tests { for _, test := range tests {
......
This diff is collapsed.
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"bytes" "bytes"
"crypto/dsa" "crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
...@@ -65,27 +66,39 @@ func TestPKCS1MismatchPublicKeyFormat(t *testing.T) { ...@@ -65,27 +66,39 @@ func TestPKCS1MismatchPublicKeyFormat(t *testing.T) {
} }
} }
func TestParsePKIXPublicKey(t *testing.T) { func testParsePKIXPublicKey(t *testing.T, pemBytes string) (pub interface{}) {
block, _ := pem.Decode([]byte(pemPublicKey)) block, _ := pem.Decode([]byte(pemBytes))
pub, err := ParsePKIXPublicKey(block.Bytes) pub, err := ParsePKIXPublicKey(block.Bytes)
if err != nil { if err != nil {
t.Errorf("Failed to parse RSA public key: %s", err) t.Fatalf("Failed to parse public key: %s", err)
return
}
rsaPub, ok := pub.(*rsa.PublicKey)
if !ok {
t.Errorf("Value returned from ParsePKIXPublicKey was not an RSA public key")
return
} }
pubBytes2, err := MarshalPKIXPublicKey(rsaPub) pubBytes2, err := MarshalPKIXPublicKey(pub)
if err != nil { if err != nil {
t.Errorf("Failed to marshal RSA public key for the second time: %s", err) t.Errorf("Failed to marshal public key for the second time: %s", err)
return return
} }
if !bytes.Equal(pubBytes2, block.Bytes) { if !bytes.Equal(pubBytes2, block.Bytes) {
t.Errorf("Reserialization of public key didn't match. got %x, want %x", pubBytes2, block.Bytes) t.Errorf("Reserialization of public key didn't match. got %x, want %x", pubBytes2, block.Bytes)
} }
return
}
func TestParsePKIXPublicKey(t *testing.T) {
t.Run("RSA", func(t *testing.T) {
pub := testParsePKIXPublicKey(t, pemPublicKey)
_, ok := pub.(*rsa.PublicKey)
if !ok {
t.Errorf("Value returned from ParsePKIXPublicKey was not an RSA public key")
}
})
t.Run("Ed25519", func(t *testing.T) {
pub := testParsePKIXPublicKey(t, pemEd25519Key)
_, ok := pub.(ed25519.PublicKey)
if !ok {
t.Errorf("Value returned from ParsePKIXPublicKey was not an Ed25519 public key")
}
})
} }
var pemPublicKey = `-----BEGIN PUBLIC KEY----- var pemPublicKey = `-----BEGIN PUBLIC KEY-----
...@@ -117,6 +130,13 @@ wg/HcAJWY60xZTJDFN+Qfx8ZQvBEin6c2/h+zZi5IVY= ...@@ -117,6 +130,13 @@ wg/HcAJWY60xZTJDFN+Qfx8ZQvBEin6c2/h+zZi5IVY=
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----
` `
// pemEd25519Key is the example from RFC 8410, Secrion 4.
var pemEd25519Key = `
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
-----END PUBLIC KEY-----
`
func TestPKIXMismatchPublicKeyFormat(t *testing.T) { func TestPKIXMismatchPublicKeyFormat(t *testing.T) {
const pkcs1PublicKey = "308201080282010100817cfed98bcaa2e2a57087451c7674e0c675686dc33ff1268b0c2a6ee0202dec710858ee1c31bdf5e7783582e8ca800be45f3275c6576adc35d98e26e95bb88ca5beb186f853b8745d88bc9102c5f38753bcda519fb05948d5c77ac429255ff8aaf27d9f45d1586e95e2e9ba8a7cb771b8a09dd8c8fed3f933fd9b439bc9f30c475953418ef25f71a2b6496f53d94d39ce850aa0cc75d445b5f5b4f4ee4db78ab197a9a8d8a852f44529a007ac0ac23d895928d60ba538b16b0b087a7f903ed29770e215019b77eaecc360f35f7ab11b6d735978795b2c4a74e5bdea4dc6594cd67ed752a108e666729a753ab36d6c4f606f8760f507e1765be8cd744007e629020103" const pkcs1PublicKey = "308201080282010100817cfed98bcaa2e2a57087451c7674e0c675686dc33ff1268b0c2a6ee0202dec710858ee1c31bdf5e7783582e8ca800be45f3275c6576adc35d98e26e95bb88ca5beb186f853b8745d88bc9102c5f38753bcda519fb05948d5c77ac429255ff8aaf27d9f45d1586e95e2e9ba8a7cb771b8a09dd8c8fed3f933fd9b439bc9f30c475953418ef25f71a2b6496f53d94d39ce850aa0cc75d445b5f5b4f4ee4db78ab197a9a8d8a852f44529a007ac0ac23d895928d60ba538b16b0b087a7f903ed29770e215019b77eaecc360f35f7ab11b6d735978795b2c4a74e5bdea4dc6594cd67ed752a108e666729a753ab36d6c4f606f8760f507e1765be8cd744007e629020103"
...@@ -518,6 +538,11 @@ func TestCreateSelfSignedCertificate(t *testing.T) { ...@@ -518,6 +538,11 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
t.Fatalf("Failed to generate ECDSA key: %s", err) t.Fatalf("Failed to generate ECDSA key: %s", err)
} }
ed25519Pub, ed25519Priv, err := ed25519.GenerateKey(random)
if err != nil {
t.Fatalf("Failed to generate Ed25519 key: %s", err)
}
tests := []struct { tests := []struct {
name string name string
pub, priv interface{} pub, priv interface{}
...@@ -531,6 +556,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) { ...@@ -531,6 +556,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
{"RSAPSS/RSAPSS", &testPrivateKey.PublicKey, testPrivateKey, true, SHA256WithRSAPSS}, {"RSAPSS/RSAPSS", &testPrivateKey.PublicKey, testPrivateKey, true, SHA256WithRSAPSS},
{"ECDSA/RSAPSS", &ecdsaPriv.PublicKey, testPrivateKey, false, SHA256WithRSAPSS}, {"ECDSA/RSAPSS", &ecdsaPriv.PublicKey, testPrivateKey, false, SHA256WithRSAPSS},
{"RSAPSS/ECDSA", &testPrivateKey.PublicKey, ecdsaPriv, false, ECDSAWithSHA384}, {"RSAPSS/ECDSA", &testPrivateKey.PublicKey, ecdsaPriv, false, ECDSAWithSHA384},
{"Ed25519", ed25519Pub, ed25519Priv, true, PureEd25519},
} }
testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth} testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth}
...@@ -1017,6 +1043,76 @@ func TestRSAPSSSelfSigned(t *testing.T) { ...@@ -1017,6 +1043,76 @@ func TestRSAPSSSelfSigned(t *testing.T) {
} }
} }
const ed25519Certificate = `
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
0c:83:d8:21:2b:82:cb:23:98:23:63:e2:f7:97:8a:43:5b:f3:bd:92
Signature Algorithm: ED25519
Issuer: CN = Ed25519 test certificate
Validity
Not Before: May 6 17:27:16 2019 GMT
Not After : Jun 5 17:27:16 2019 GMT
Subject: CN = Ed25519 test certificate
Subject Public Key Info:
Public Key Algorithm: ED25519
ED25519 Public-Key:
pub:
36:29:c5:6c:0d:4f:14:6c:81:d0:ff:75:d3:6a:70:
5f:69:cd:0f:4d:66:d5:da:98:7e:82:49:89:a3:8a:
3c:fa
X509v3 extensions:
X509v3 Subject Key Identifier:
09:3B:3A:9D:4A:29:D8:95:FF:68:BE:7B:43:54:72:E0:AD:A2:E3:AE
X509v3 Authority Key Identifier:
keyid:09:3B:3A:9D:4A:29:D8:95:FF:68:BE:7B:43:54:72:E0:AD:A2:E3:AE
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: ED25519
53:a5:58:1c:2c:3b:2a:9e:ac:9d:4e:a5:1d:5f:5d:6d:a6:b5:
08:de:12:82:f3:97:20:ae:fa:d8:98:f4:1a:83:32:6b:91:f5:
24:1d:c4:20:7f:2c:e2:4d:da:13:3b:6d:54:1a:d2:a8:28:dc:
60:b9:d4:f4:78:4b:3c:1c:91:00
-----BEGIN CERTIFICATE-----
MIIBWzCCAQ2gAwIBAgIUDIPYISuCyyOYI2Pi95eKQ1vzvZIwBQYDK2VwMCMxITAf
BgNVBAMMGEVkMjU1MTkgdGVzdCBjZXJ0aWZpY2F0ZTAeFw0xOTA1MDYxNzI3MTZa
Fw0xOTA2MDUxNzI3MTZaMCMxITAfBgNVBAMMGEVkMjU1MTkgdGVzdCBjZXJ0aWZp
Y2F0ZTAqMAUGAytlcAMhADYpxWwNTxRsgdD/ddNqcF9pzQ9NZtXamH6CSYmjijz6
o1MwUTAdBgNVHQ4EFgQUCTs6nUop2JX/aL57Q1Ry4K2i464wHwYDVR0jBBgwFoAU
CTs6nUop2JX/aL57Q1Ry4K2i464wDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQBT
pVgcLDsqnqydTqUdX11tprUI3hKC85cgrvrYmPQagzJrkfUkHcQgfyziTdoTO21U
GtKoKNxgudT0eEs8HJEA
-----END CERTIFICATE-----`
func TestEd25519SelfSigned(t *testing.T) {
der, _ := pem.Decode([]byte(ed25519Certificate))
if der == nil {
t.Fatalf("Failed to find PEM block")
}
cert, err := ParseCertificate(der.Bytes)
if err != nil {
t.Fatalf("Failed to parse: %s", err)
}
if cert.PublicKeyAlgorithm != Ed25519 {
t.Fatalf("Parsed key algorithm was not Ed25519")
}
parsedKey, ok := cert.PublicKey.(ed25519.PublicKey)
if !ok {
t.Fatalf("Parsed key was not an Ed25519 key: %s", err)
}
if len(parsedKey) != ed25519.PublicKeySize {
t.Fatalf("Invalid Ed25519 key")
}
if err = cert.CheckSignatureFrom(cert); err != nil {
t.Fatalf("Signature check failed: %s", err)
}
}
const pemCertificate = `-----BEGIN CERTIFICATE----- const pemCertificate = `-----BEGIN CERTIFICATE-----
MIIDATCCAemgAwIBAgIRAKQkkrFx1T/dgB/Go/xBM5swDQYJKoZIhvcNAQELBQAw MIIDATCCAemgAwIBAgIRAKQkkrFx1T/dgB/Go/xBM5swDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNjA4MTcyMDM2MDdaFw0xNzA4MTcyMDM2 EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNjA4MTcyMDM2MDdaFw0xNzA4MTcyMDM2
...@@ -1176,6 +1272,11 @@ func TestCreateCertificateRequest(t *testing.T) { ...@@ -1176,6 +1272,11 @@ func TestCreateCertificateRequest(t *testing.T) {
t.Fatalf("Failed to generate ECDSA key: %s", err) t.Fatalf("Failed to generate ECDSA key: %s", err)
} }
_, ed25519Priv, err := ed25519.GenerateKey(random)
if err != nil {
t.Fatalf("Failed to generate Ed25519 key: %s", err)
}
tests := []struct { tests := []struct {
name string name string
priv interface{} priv interface{}
...@@ -1185,6 +1286,7 @@ func TestCreateCertificateRequest(t *testing.T) { ...@@ -1185,6 +1286,7 @@ func TestCreateCertificateRequest(t *testing.T) {
{"ECDSA-256", ecdsa256Priv, ECDSAWithSHA1}, {"ECDSA-256", ecdsa256Priv, ECDSAWithSHA1},
{"ECDSA-384", ecdsa384Priv, ECDSAWithSHA1}, {"ECDSA-384", ecdsa384Priv, ECDSAWithSHA1},
{"ECDSA-521", ecdsa521Priv, ECDSAWithSHA1}, {"ECDSA-521", ecdsa521Priv, ECDSAWithSHA1},
{"Ed25519", ed25519Priv, PureEd25519},
} }
for _, test := range tests { for _, test := range tests {
......
...@@ -403,7 +403,7 @@ var pkgDeps = map[string][]string{ ...@@ -403,7 +403,7 @@ var pkgDeps = map[string][]string{
"container/list", "crypto/x509", "encoding/pem", "net", "syscall", "container/list", "crypto/x509", "encoding/pem", "net", "syscall",
}, },
"crypto/x509": { "crypto/x509": {
"L4", "CRYPTO-MATH", "OS", "CGO", "L4", "CRYPTO-MATH", "OS", "CGO", "crypto/ed25519",
"crypto/x509/pkix", "encoding/pem", "encoding/hex", "net", "os/user", "syscall", "net/url", "crypto/x509/pkix", "encoding/pem", "encoding/hex", "net", "os/user", "syscall", "net/url",
"golang.org/x/crypto/cryptobyte", "golang.org/x/crypto/cryptobyte/asn1", "golang.org/x/crypto/cryptobyte", "golang.org/x/crypto/cryptobyte/asn1",
}, },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment