Complete x509parse_public_key_ec()

Warning: due to a bug in oid_descriptor_from_buf(), keys associated to some
curves (secp224r1, secp384r1, secp521r1) are incorrectly rejected,
since their namedCurve OID contains a nul byte.
diff --git a/library/x509parse.c b/library/x509parse.c
index 840319f..e04d56b 100644
--- a/library/x509parse.c
+++ b/library/x509parse.c
@@ -234,6 +234,40 @@
 }
 
 /*
+ * subjectPublicKey  BIT STRING
+ * -- which, in our case, contains
+ * ECPoint ::= octet string (not ASN.1)
+ */
+static int x509_get_subpubkey_ec( unsigned char **p, const unsigned char *end,
+                                  const ecp_group *grp, ecp_point *pt )
+{
+    int ret;
+    size_t len;
+
+    if( ( ret = asn1_get_tag( p, end, &len, ASN1_BIT_STRING ) ) != 0 )
+        return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret );
+
+    if( *p + len != end )
+        return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT +
+                POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
+
+    /*
+     * First byte in the content of BIT STRING is the nummber of padding bit.
+     * Here it is always 0 since ECPoint is an octet string, so skip it.
+     */
+    ++*p;
+    --len;
+
+    if( ( ret = ecp_point_read_binary( grp, pt,
+                    (const unsigned char *) *p, len ) ) != 0 )
+    {
+        return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret );
+    }
+
+    return( 0 );
+}
+
+/*
  *  AttributeTypeAndValue ::= SEQUENCE {
  *    type     AttributeType,
  *    value    AttributeValue }
@@ -2634,20 +2668,71 @@
 }
 
 /*
+ * Parse a public EC key in RFC 5480 format, der-encoded
+ */
+static int x509parse_public_key_ec_der( ecp_keypair *key,
+                                        const unsigned char *buf, size_t len )
+{
+    int ret;
+    ecp_group_id grp_id;
+    x509_buf alg_oid;
+    pk_type_t alg = POLARSSL_PK_NONE;
+    unsigned char *p = (unsigned char *) buf;
+    unsigned char *end = p + len;
+    const unsigned char *params_end;
+    /*
+     * SubjectPublicKeyInfo  ::=  SEQUENCE  {
+     *   algorithm         AlgorithmIdentifier,
+     *   subjectPublicKey  BIT STRING
+     * }
+     * -- algorithm parameters are ECParameters
+     * -- subjectPublicKey is an ECPoint
+     */
+    if( ( ret = asn1_get_tag( &p, end, &len,
+                    ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
+    {
+        return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret );
+    }
+
+    if( ( ret = x509_get_alg( &p, end, &alg_oid, &params_end ) ) != 0 )
+        return( ret );
+
+    if( oid_get_pk_alg( &alg_oid, &alg ) != 0 )
+        return( POLARSSL_ERR_X509_UNKNOWN_PK_ALG );
+
+    if( alg != POLARSSL_PK_ECKEY && alg != POLARSSL_PK_ECKEY_DH )
+        return( POLARSSL_ERR_X509_UNKNOWN_PK_ALG );
+
+    if ( alg == POLARSSL_PK_ECKEY_DH )
+        key->alg = POLARSSL_ECP_KEY_ALG_ECDH;
+
+    if( ( ret = x509_get_ecparams( &p, params_end, &grp_id ) ) != 0 )
+        return( ret );
+
+    if( ( ret = ecp_use_known_dp( &key->grp, grp_id ) ) != 0 )
+        return( ret );
+
+    if( ( ret = x509_get_subpubkey_ec( &p, end, &key->grp, &key->Q ) ) != 0 )
+    {
+        return( ret );
+    }
+
+    return( 0 );
+}
+
+/*
  * Parse a public EC key
  */
 int x509parse_public_key_ec( ecp_keypair *eckey,
                              const unsigned char *key, size_t keylen )
 {
     int ret;
-    size_t len;
-    unsigned char *p, *end;
-    x509_buf alg_oid;
 #if defined(POLARSSL_PEM_C)
+    size_t len;
     pem_context pem;
 
     pem_init( &pem );
-    ret = pem_read_buffer( &pem, /* TODO: check header */
+    ret = pem_read_buffer( &pem,
             "-----BEGIN PUBLIC KEY-----",
             "-----END PUBLIC KEY-----",
             key, NULL, 0, &len );
@@ -2657,6 +2742,7 @@
         /*
          * Was PEM encoded
          */
+        key = pem.buf;
         keylen = pem.buflen;
     }
     else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT )
@@ -2664,32 +2750,19 @@
         pem_free( &pem );
         return( ret );
     }
-
-    p = ( ret == 0 ) ? pem.buf : (unsigned char *) key;
-#else
-    p = (unsigned char *) key;
 #endif
-    end = p + keylen;
 
-    /* TODO: parse key */
-    (void) alg_oid;
-    (void) end;
-    (void) eckey;
-
-    if( ( ret = ecp_check_pubkey( &eckey->grp, &eckey->Q ) ) != 0 )
+    if( ( ret = x509parse_public_key_ec_der ( eckey, key, keylen )  ) != 0 ||
+        ( ret = ecp_check_pubkey( &eckey->grp, &eckey->Q )          ) != 0 )
     {
-#if defined(POLARSSL_PEM_C)
-        pem_free( &pem );
-#endif
         ecp_keypair_free( eckey );
-        return( ret );
     }
 
 #if defined(POLARSSL_PEM_C)
     pem_free( &pem );
 #endif
 
-    return( 0 );
+    return( ret );
 }
 #endif /* defined(POLARSSL_ECP_C) */
 
diff --git a/tests/data_files/ec_pub.der b/tests/data_files/ec_pub.der
index 954bf3b..74c5951 100644
--- a/tests/data_files/ec_pub.der
+++ b/tests/data_files/ec_pub.der
Binary files differ
diff --git a/tests/data_files/ec_pub.pem b/tests/data_files/ec_pub.pem
index dbbc2e6..d677d27 100644
--- a/tests/data_files/ec_pub.pem
+++ b/tests/data_files/ec_pub.pem
@@ -1,4 +1,4 @@
 -----BEGIN PUBLIC KEY-----
-ME4wEAYHKoZIzj0CAQYFK4EEACEDOgAEb3GKqedAJ2/1CRdhjIRhQO+4erL49GlI
-QrBg7frKWrc6ZrnjgXgdHt68Tux/2IyRuff+lvGn32s=
+MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEvHl9s65/COw9SWtPtBGz9iClWKUB
+4CItCM/g3Irsixp78kvpKVHMW6G+uyR0kJrg
 -----END PUBLIC KEY-----