]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/x509: Add ECDSA support
authorBenjamin Black <b@b3k.us>
Tue, 22 May 2012 15:03:59 +0000 (11:03 -0400)
committerAdam Langley <agl@golang.org>
Tue, 22 May 2012 15:03:59 +0000 (11:03 -0400)
R=golang-dev, agl, rsc
CC=golang-dev
https://golang.org/cl/6208087

src/pkg/crypto/x509/x509.go
src/pkg/crypto/x509/x509_test.go

index c4d85e67f0c28667229388c19a061fe0540367b8..6848bf801d440b697903ef017c5c15fc2e078006 100644 (file)
@@ -9,6 +9,8 @@ import (
        "bytes"
        "crypto"
        "crypto/dsa"
+       "crypto/ecdsa"
+       "crypto/elliptic"
        "crypto/rsa"
        "crypto/sha1"
        "crypto/x509/pkix"
@@ -106,6 +108,8 @@ type dsaSignature struct {
        R, S *big.Int
 }
 
+type ecdsaSignature dsaSignature
+
 type validity struct {
        NotBefore, NotAfter time.Time
 }
@@ -133,6 +137,10 @@ const (
        SHA512WithRSA
        DSAWithSHA1
        DSAWithSHA256
+       ECDSAWithSHA1
+       ECDSAWithSHA256
+       ECDSAWithSHA384
+       ECDSAWithSHA512
 )
 
 type PublicKeyAlgorithm int
@@ -141,6 +149,7 @@ const (
        UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota
        RSA
        DSA
+       ECDSA
 )
 
 // OIDs for signature algorithms
@@ -160,6 +169,12 @@ const (
 // dsaWithSha1 OBJECT IDENTIFIER ::= {
 //    iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 } 
 //
+// RFC 3279 2.2.3 ECDSA Signature Algorithm
+// 
+// ecdsa-with-SHA1 OBJECT IDENTIFIER ::= {
+//       iso(1) member-body(2) us(840) ansi-x962(10045)
+//    signatures(4) ecdsa-with-SHA1(1)}
+//
 //
 // RFC 4055 5 PKCS #1 Version 1.5
 // 
@@ -176,15 +191,30 @@ const (
 //    joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101)
 //    csor(3) algorithms(4) id-dsa-with-sha2(3) 2}
 //
+// RFC 5758 3.2 ECDSA Signature Algorithm
+//
+// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+//    us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
+//
+// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+//    us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 }
+//
+// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+//    us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 }
+
 var (
-       oidSignatureMD2WithRSA    = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
-       oidSignatureMD5WithRSA    = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
-       oidSignatureSHA1WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
-       oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
-       oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
-       oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
-       oidSignatureDSAWithSHA1   = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
-       oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2}
+       oidSignatureMD2WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
+       oidSignatureMD5WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
+       oidSignatureSHA1WithRSA     = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
+       oidSignatureSHA256WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
+       oidSignatureSHA384WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
+       oidSignatureSHA512WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
+       oidSignatureDSAWithSHA1     = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
+       oidSignatureDSAWithSHA256   = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2}
+       oidSignatureECDSAWithSHA1   = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
+       oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
+       oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
+       oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
 )
 
 func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm {
@@ -205,6 +235,14 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm
                return DSAWithSHA1
        case oid.Equal(oidSignatureDSAWithSHA256):
                return DSAWithSHA256
+       case oid.Equal(oidSignatureECDSAWithSHA1):
+               return ECDSAWithSHA1
+       case oid.Equal(oidSignatureECDSAWithSHA256):
+               return ECDSAWithSHA256
+       case oid.Equal(oidSignatureECDSAWithSHA384):
+               return ECDSAWithSHA384
+       case oid.Equal(oidSignatureECDSAWithSHA512):
+               return ECDSAWithSHA512
        }
        return UnknownSignatureAlgorithm
 }
@@ -218,9 +256,15 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm
 //
 // id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
 //    x9-57(10040) x9cm(4) 1 }
+//
+// RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters
+//
+// id-ecPublicKey OBJECT IDENTIFIER ::= {
+//       iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
 var (
-       oidPublicKeyRsa = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
-       oidPublicKeyDsa = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
+       oidPublicKeyRsa   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
+       oidPublicKeyDsa   = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
+       oidPublicKeyEcdsa = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
 )
 
 func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm {
@@ -229,10 +273,49 @@ func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm
                return RSA
        case oid.Equal(oidPublicKeyDsa):
                return DSA
+       case oid.Equal(oidPublicKeyEcdsa):
+               return ECDSA
        }
        return UnknownPublicKeyAlgorithm
 }
 
+// RFC 5480, 2.1.1.1. Named Curve
+//
+// secp224r1 OBJECT IDENTIFIER ::= {
+//   iso(1) identified-organization(3) certicom(132) curve(0) 33 }
+//
+// secp256r1 OBJECT IDENTIFIER ::= {
+//   iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3)
+//   prime(1) 7 }
+//
+// secp384r1 OBJECT IDENTIFIER ::= {
+//   iso(1) identified-organization(3) certicom(132) curve(0) 34 }
+//
+// secp521r1 OBJECT IDENTIFIER ::= {
+//   iso(1) identified-organization(3) certicom(132) curve(0) 35 }
+//
+// NB: secp256r1 is equivalent to prime256v1
+var (
+       oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33}
+       oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
+       oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
+       oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
+)
+
+func getNamedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {
+       switch {
+       case oid.Equal(oidNamedCurveP224):
+               return elliptic.P224()
+       case oid.Equal(oidNamedCurveP256):
+               return elliptic.P256()
+       case oid.Equal(oidNamedCurveP384):
+               return elliptic.P384()
+       case oid.Equal(oidNamedCurveP521):
+               return elliptic.P521()
+       }
+       return nil
+}
+
 // KeyUsage represents the set of actions that are valid for a given key. It's
 // a bitmap of the KeyUsage* constants.
 type KeyUsage int
@@ -376,13 +459,13 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
        var hashType crypto.Hash
 
        switch algo {
-       case SHA1WithRSA, DSAWithSHA1:
+       case SHA1WithRSA, DSAWithSHA1, ECDSAWithSHA1:
                hashType = crypto.SHA1
-       case SHA256WithRSA, DSAWithSHA256:
+       case SHA256WithRSA, DSAWithSHA256, ECDSAWithSHA256:
                hashType = crypto.SHA256
-       case SHA384WithRSA:
+       case SHA384WithRSA, ECDSAWithSHA384:
                hashType = crypto.SHA384
-       case SHA512WithRSA:
+       case SHA512WithRSA, ECDSAWithSHA512:
                hashType = crypto.SHA512
        default:
                return ErrUnsupportedAlgorithm
@@ -411,6 +494,18 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
                        return errors.New("DSA verification failure")
                }
                return
+       case *ecdsa.PublicKey:
+               ecdsaSig := new(ecdsaSignature)
+               if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil {
+                       return err
+               }
+               if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
+                       return errors.New("crypto/x509: ECDSA signature contained zero or negative values")
+               }
+               if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) {
+                       return errors.New("crypto/x509: ECDSA verification failure")
+               }
+               return
        }
        return ErrUnsupportedAlgorithm
 }
@@ -489,6 +584,27 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{
                        Y: p,
                }
                return pub, nil
+       case ECDSA:
+               paramsData := keyData.Algorithm.Parameters.FullBytes
+               namedCurveOID := new(asn1.ObjectIdentifier)
+               _, err := asn1.Unmarshal(paramsData, namedCurveOID)
+               if err != nil {
+                       return nil, err
+               }
+               namedCurve := getNamedCurveFromOID(*namedCurveOID)
+               if namedCurve == nil {
+                       return nil, errors.New("crypto/x509: unsupported elliptic curve")
+               }
+               x, y := elliptic.Unmarshal(namedCurve, asn1Data)
+               if x == nil {
+                       return nil, errors.New("crypto/x509: failed to unmarshal elliptic curve point")
+               }
+               pub := &ecdsa.PublicKey{
+                       Curve: namedCurve,
+                       X:     x,
+                       Y:     y,
+               }
+               return pub, nil
        default:
                return nil, nil
        }
index f0327b0124d42bdfca67d079f3f7943cd5999163..46d04dc999f565c45a038a27e7d269f621d91250 100644 (file)
@@ -7,8 +7,11 @@ package x509
 import (
        "bytes"
        "crypto/dsa"
+       "crypto/ecdsa"
        "crypto/rand"
        "crypto/rsa"
+       _ "crypto/sha256"
+       _ "crypto/sha512"
        "crypto/x509/pkix"
        "encoding/asn1"
        "encoding/base64"
@@ -299,6 +302,114 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
        }
 }
 
+// Self-signed certificate using ECDSA with SHA1 & secp256r1
+var ecdsaSHA1CertPem = `
+-----BEGIN CERTIFICATE-----
+MIICDjCCAbUCCQDF6SfN0nsnrjAJBgcqhkjOPQQBMIGPMQswCQYDVQQGEwJVUzET
+MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEVMBMG
+A1UECgwMR29vZ2xlLCBJbmMuMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
+CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIwMjAyMDUw
+WhcNMjIwNTE4MjAyMDUwWjCBjzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
+b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTATBgNVBAoMDEdvb2dsZSwg
+SW5jLjEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20xIzAhBgkqhkiG9w0BCQEWFGdv
+bGFuZy1kZXZAZ21haWwuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/Wgn
+WQDo5+bz71T0327ERgd5SDDXFbXLpzIZDXTkjpe8QTEbsF+ezsQfrekrpDPC4Cd3
+P9LY0tG+aI8IyVKdUjAJBgcqhkjOPQQBA0gAMEUCIGlsqMcRqWVIWTD6wXwe6Jk2
+DKxL46r/FLgJYnzBEH99AiEA3fBouObsvV1R3oVkb4BQYnD4/4LeId6lAT43YvyV
+a/A=
+-----END CERTIFICATE-----
+`
+
+// Self-signed certificate using ECDSA with SHA256 & secp256r1
+var ecdsaSHA256p256CertPem = `
+-----BEGIN CERTIFICATE-----
+MIICDzCCAbYCCQDlsuMWvgQzhTAKBggqhkjOPQQDAjCBjzELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
+BgNVBAoMDEdvb2dsZSwgSW5jLjEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20xIzAh
+BgkqhkiG9w0BCQEWFGdvbGFuZy1kZXZAZ21haWwuY29tMB4XDTEyMDUyMTAwMTkx
+NloXDTIyMDUxOTAwMTkxNlowgY8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp
+Zm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYDVQQKDAxHb29nbGUs
+IEluYy4xFzAVBgNVBAMMDnd3dy5nb29nbGUuY29tMSMwIQYJKoZIhvcNAQkBFhRn
+b2xhbmctZGV2QGdtYWlsLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPMt
+2ErhxAty5EJRu9yM+MTy+hUXm3pdW1ensAv382KoGExSXAFWP7pjJnNtHO+XSwVm
+YNtqjcAGFKpweoN//kQwCgYIKoZIzj0EAwIDRwAwRAIgIYSaUA/IB81gjbIw/hUV
+70twxJr5EcgOo0hLp3Jm+EYCIFDO3NNcgmURbJ1kfoS3N/0O+irUtoPw38YoNkqJ
+h5wi
+-----END CERTIFICATE-----
+`
+
+// Self-signed certificate using ECDSA with SHA256 & secp384r1
+var ecdsaSHA256p384CertPem = `
+-----BEGIN CERTIFICATE-----
+MIICSjCCAdECCQDje/no7mXkVzAKBggqhkjOPQQDAjCBjjELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS
+BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
+CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMDYxMDM0
+WhcNMjIwNTE5MDYxMDM0WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
+b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg
+SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s
+YW5nLWRldkBnbWFpbC5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARRuzRNIKRK
+jIktEmXanNmrTR/q/FaHXLhWRZ6nHWe26Fw7Rsrbk+VjGy4vfWtNn7xSFKrOu5ze
+qxKnmE0h5E480MNgrUiRkaGO2GMJJVmxx20aqkXOk59U8yGA4CghE6MwCgYIKoZI
+zj0EAwIDZwAwZAIwBZEN8gvmRmfeP/9C1PRLzODIY4JqWub2PLRT4mv9GU+yw3Gr
+PU9A3CHMdEcdw/MEAjBBO1lId8KOCh9UZunsSMfqXiVurpzmhWd6VYZ/32G+M+Mh
+3yILeYQzllt/g0rKVRk=
+-----END CERTIFICATE-----
+`
+
+// Self-signed certificate using ECDSA with SHA384 & secp521r1
+var ecdsaSHA384p521CertPem = `
+-----BEGIN CERTIFICATE-----
+MIICljCCAfcCCQDhp1AFD/ahKjAKBggqhkjOPQQDAzCBjjELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS
+BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
+CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMTUwNDI5
+WhcNMjIwNTE5MTUwNDI5WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
+b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg
+SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s
+YW5nLWRldkBnbWFpbC5jb20wgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABACqx9Rv
+IssRs1LWYcNN+WffwlHw4Tv3y8/LIAA9MF1ZScIonU9nRMxt4a2uGJVCPDw6JHpz
+PaYc0E9puLoE9AfKpwFr59Jkot7dBg55SKPEFkddoip/rvmN7NPAWjMBirOwjOkm
+8FPthvPhGPqsu9AvgVuHu3PosWiHGNrhh379pva8MzAKBggqhkjOPQQDAwOBjAAw
+gYgCQgEHNmswkUdPpHqrVxp9PvLVl+xxPuHBkT+75z9JizyxtqykHQo9Uh6SWCYH
+BF9KLolo01wMt8DjoYP5Fb3j5MH7xwJCAbWZzTOp4l4DPkIvAh4LeC4VWbwPPyqh
+kBg71w/iEcSY3wUKgHGcJJrObZw7wys91I5kENljqw/Samdr3ka+jBJa
+-----END CERTIFICATE-----
+`
+
+var ecdsaTests = []struct {
+       sigAlgo SignatureAlgorithm
+       pemCert string
+}{
+       {ECDSAWithSHA1, ecdsaSHA1CertPem},
+       {ECDSAWithSHA256, ecdsaSHA256p256CertPem},
+       {ECDSAWithSHA256, ecdsaSHA256p384CertPem},
+       {ECDSAWithSHA384, ecdsaSHA384p521CertPem},
+}
+
+func TestECDSA(t *testing.T) {
+       for i, test := range ecdsaTests {
+               pemBlock, _ := pem.Decode([]byte(test.pemCert))
+               cert, err := ParseCertificate(pemBlock.Bytes)
+               if err != nil {
+                       t.Errorf("%d: failed to parse certificate: %s", i, err)
+                       continue
+               }
+               if sa := cert.SignatureAlgorithm; sa != test.sigAlgo {
+                       t.Errorf("%d: signature algorithm is %v, want %v", i, sa, test.sigAlgo)
+               }
+               if parsedKey, ok := cert.PublicKey.(*ecdsa.PublicKey); !ok {
+                       t.Errorf("%d: wanted an ECDSA public key but found: %#v", i, parsedKey)
+               }
+               if pka := cert.PublicKeyAlgorithm; pka != ECDSA {
+                       t.Errorf("%d: public key algorithm is %v, want ECDSA", i, pka)
+               }
+               if err = cert.CheckSignatureFrom(cert); err != nil {
+                       t.Errorf("%d: certificate verfication failed: %s", i, err)
+               }
+       }
+}
+
 // Self-signed certificate using DSA with SHA1
 var dsaCertPem = `-----BEGIN CERTIFICATE-----
 MIIEDTCCA82gAwIBAgIJALHPghaoxeDhMAkGByqGSM44BAMweTELMAkGA1UEBhMC