Suppport otherName of type hardware module name

Add support of parsing of subject alternative name, of type otherName.
Currently supports only hardware module name, as defined in rfc 4108.
diff --git a/library/x509_crt.c b/library/x509_crt.c
index 97a06d5..f9cbed0 100644
--- a/library/x509_crt.c
+++ b/library/x509_crt.c
@@ -618,7 +618,8 @@
  *      nameAssigner            [0]     DirectoryString OPTIONAL,
  *      partyName               [1]     DirectoryString }
  *
- * NOTE: we only parse and use dNSName at this point.
+ * NOTE: we only parse and use dNSName at this point,
+ * and otherName of type HwModuleName, as defined in RFC 4108.
  */
 static int x509_get_subject_alt_name( unsigned char **p,
                                       const unsigned char *end,
@@ -657,13 +658,6 @@
                     MBEDTLS_ERR_ASN1_UNEXPECTED_TAG );
         }
 
-        /* Skip everything but DNS name */
-        if( tag != ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | 2 ) )
-        {
-            *p += tag_len;
-            continue;
-        }
-
         /* Allocate and assign next pointer */
         if( cur->buf.p != NULL )
         {
@@ -1435,32 +1429,195 @@
 }
 #endif /* MBEDTLS_FS_IO */
 
+/*
+ * OtherName ::= SEQUENCE {
+ *      type-id    OBJECT IDENTIFIER,
+ *      value      [0] EXPLICIT ANY DEFINED BY type-id }
+ *
+ * HardwareModuleName ::= SEQUENCE {
+ *                           hwType OBJECT IDENTIFIER,
+ *                           hwSerialNum OCTET STRING }
+ *
+ * NOTE: we currently only parse and use otherName of type HwModuleName,
+ * as defined in RFC 4108.
+ */
+static int x509_get_other_name( const mbedtls_x509_buf *subject_alt_name,
+                                mbedtls_x509_san_other_name *other_name )
+{
+    int ret;
+    size_t len;
+    unsigned char *p = subject_alt_name->p;
+    const unsigned char *end = p + subject_alt_name->len;
+    mbedtls_x509_buf cur_oid;
+
+    if( ( subject_alt_name->tag &
+        ( MBEDTLS_ASN1_TAG_CLASS_MASK | MBEDTLS_ASN1_TAG_VALUE_MASK ) ) !=
+        ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_X509_SAN_OTHER_NAME ) )
+    {
+        /*
+         * The given subject alternative name is not of type "othername".
+         */
+        return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
+    }
+
+    if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
+                                      MBEDTLS_ASN1_OID ) ) != 0 )
+        return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
+
+    cur_oid.tag = MBEDTLS_ASN1_OID;
+    cur_oid.p = p;
+    cur_oid.len = len;
+
+    /*
+     * Only HwModuleName is currently supported.
+     */
+    if( MBEDTLS_OID_CMP( MBEDTLS_OID_ON_HW_MODULE_NAME, &cur_oid ) != 0 )
+    {
+        return( MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE );
+    }
+
+    if( p + len >= end )
+    {
+        mbedtls_platform_zeroize( other_name, sizeof( other_name ) );
+        return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
+                MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
+    }
+    p += len;
+    if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
+            MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC ) ) != 0 )
+        return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
+
+    if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
+                     MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
+       return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
+
+    if( ( ret = mbedtls_asn1_get_tag( &p, end, &len, MBEDTLS_ASN1_OID ) ) != 0 )
+        return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
+
+    other_name->value.hardware_module_name.oid.tag = MBEDTLS_ASN1_OID;
+    other_name->value.hardware_module_name.oid.p = p;
+    other_name->value.hardware_module_name.oid.len = len;
+
+    if( p + len >= end )
+    {
+        mbedtls_platform_zeroize( other_name, sizeof( other_name ) );
+        return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
+                MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
+    }
+    p += len;
+    if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
+                                      MBEDTLS_ASN1_OCTET_STRING ) ) != 0 )
+        return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
+
+    other_name->value.hardware_module_name.val.tag = MBEDTLS_ASN1_OCTET_STRING;
+    other_name->value.hardware_module_name.val.p = p;
+    other_name->value.hardware_module_name.val.len = len;
+    other_name->value.hardware_module_name.next = NULL;
+    other_name->value.hardware_module_name.next_merged = 0;
+    p += len;
+    if( p != end )
+    {
+        mbedtls_platform_zeroize( other_name,
+                                  sizeof( other_name ) );
+        return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
+                MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
+    }
+    return( 0 );
+}
+
 static int x509_info_subject_alt_name( char **buf, size_t *size,
-                                       const mbedtls_x509_sequence *subject_alt_name )
+                                       const mbedtls_x509_sequence *subject_alt_name,
+                                       const char *prefix )
 {
     size_t i;
+    int ret;
     size_t n = *size;
     char *p = *buf;
     const mbedtls_x509_sequence *cur = subject_alt_name;
-    const char *sep = "";
-    size_t sep_len = 0;
 
     while( cur != NULL )
     {
-        if( cur->buf.len + sep_len >= n )
+        switch( cur->buf.tag &
+                ( MBEDTLS_ASN1_TAG_CLASS_MASK | MBEDTLS_ASN1_TAG_VALUE_MASK ) )
         {
-            *p = '\0';
-            return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL );
+            /*
+             * otherName
+             */
+            case( MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_X509_SAN_OTHER_NAME ):
+            {
+                mbedtls_x509_san_other_name other_name;
+
+                ret = x509_get_other_name( &cur->buf, &other_name );
+                if( ret != 0 )
+                {
+                    /*
+                     * In case MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE is returned,
+                     * then the "otherName" is of an unsupported type. Ignore.
+                     */
+                    if( ret == MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE )
+                        ret = 0;
+                    return( ret );
+                }
+
+                ret = mbedtls_snprintf( p, n, "\n%s    otherName :", prefix );
+                MBEDTLS_X509_SAFE_SNPRINTF;
+
+                if( MBEDTLS_OID_CMP( MBEDTLS_OID_ON_HW_MODULE_NAME,
+                             &other_name.value.hardware_module_name.oid ) != 0 )
+                {
+                    ret = mbedtls_snprintf( p, n, "\n%s        hardware module name :", prefix );
+                    MBEDTLS_X509_SAFE_SNPRINTF;
+                    ret = mbedtls_snprintf( p, n, "\n%s            hardware type          : ", prefix );
+                    MBEDTLS_X509_SAFE_SNPRINTF;
+
+                    ret = mbedtls_oid_get_numeric_string( p, n, &other_name.value.hardware_module_name.oid );
+                    MBEDTLS_X509_SAFE_SNPRINTF;
+
+                    ret = mbedtls_snprintf( p, n, "\n%s            hardware serial number : ", prefix );
+                    MBEDTLS_X509_SAFE_SNPRINTF;
+
+                    if( other_name.value.hardware_module_name.val.len >= n )
+                    {
+                        *p = '\0';
+                        return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL );
+                    }
+
+                    for( i = 0; i < other_name.value.hardware_module_name.val.len; i++ )
+                    {
+                        *p++ = other_name.value.hardware_module_name.val.p[i];
+                    }
+                    n -= other_name.value.hardware_module_name.val.len;
+
+                }/* MBEDTLS_OID_ON_HW_MODULE_NAME */
+            }
+            break;
+
+            /*
+             * dNSName
+             */
+            case( MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_X509_SAN_DNS_NAME ):
+            {
+
+                ret = mbedtls_snprintf( p, n, "\n%s    dNSName : ", prefix );
+                MBEDTLS_X509_SAFE_SNPRINTF;
+                if( cur->buf.len >= n )
+                {
+                    *p = '\0';
+                    return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL );
+                }
+                n -= cur->buf.len;
+                for( i = 0; i < cur->buf.len; i++ )
+                    *p++ = cur->buf.p[i];
+            }
+            break;
+
+            /*
+             * Type not supported, skip item.
+             */
+            default:
+                break;
         }
 
-        n -= cur->buf.len + sep_len;
-        for( i = 0; i < sep_len; i++ )
-            *p++ = sep[i];
-        for( i = 0; i < cur->buf.len; i++ )
-            *p++ = cur->buf.p[i];
-
-        sep = ", ";
-        sep_len = 2;
 
         cur = cur->next;
     }
@@ -1473,6 +1630,105 @@
     return( 0 );
 }
 
+int mbedtls_x509_parse_subject_alternative_name( const mbedtls_x509_crt *crt,
+                                                 mbedtls_x509_subject_alternative_name **san )
+{
+    int ret;
+    const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
+    mbedtls_x509_subject_alternative_name *cur_san = *san, *prev_san = NULL;
+
+    if( crt->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME )
+    {
+        if( cur_san != NULL )
+        {
+            return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
+        }
+
+        while( cur != NULL )
+        {
+            switch( cur->buf.tag &
+                    ( MBEDTLS_ASN1_TAG_CLASS_MASK |
+                      MBEDTLS_ASN1_TAG_VALUE_MASK ) )
+            {
+                /*
+                 * otherName
+                 */
+                case( MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_X509_SAN_OTHER_NAME ):
+                {
+                    mbedtls_x509_san_other_name other_name;
+
+                    ret = x509_get_other_name( &cur->buf, &other_name );
+                    if( ret != 0 )
+                    {
+                        /*
+                         * In case MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE is returned,
+                         * then the "otherName" is of an unsupported type. Ignore.
+                         */
+                        if( ret == MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE )
+                            ret = 0;
+                        cur = cur->next;
+                        continue;
+                    }
+
+                    cur_san = mbedtls_calloc( 1, sizeof( mbedtls_x509_subject_alternative_name ) );
+                    if( cur_san == NULL )
+                        return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
+                                MBEDTLS_ERR_ASN1_ALLOC_FAILED );
+
+                    if( prev_san != NULL )
+                        prev_san->next = cur_san;
+
+                    cur_san->type = MBEDTLS_X509_SAN_OTHER_NAME;
+                    memcpy( &cur_san->san.other_name,
+                            &other_name, sizeof( other_name ) );
+
+                }
+                break;
+
+                /*
+                 * dNSName
+                 */
+                case( MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_X509_SAN_DNS_NAME ):
+                {
+                    cur_san = mbedtls_calloc( 1, sizeof( mbedtls_x509_subject_alternative_name ) );
+                    if( cur_san == NULL )
+                        return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
+                                MBEDTLS_ERR_ASN1_ALLOC_FAILED );
+
+                    if( prev_san != NULL )
+                        prev_san->next = cur_san;
+
+                    memset( cur_san, 0, sizeof( mbedtls_x509_subject_alternative_name ) );
+                    cur_san->type = MBEDTLS_X509_SAN_DNS_NAME;
+
+                    memcpy( &cur_san->san.unstructured_name,
+                            &cur->buf, sizeof( cur->buf ) );
+
+                }
+                break;
+
+                /*
+                 * Type not supported, skip item.
+                 */
+                default:
+                    break;
+            }
+
+            if( *san == NULL )
+                *san = cur_san;
+
+            if( cur_san != NULL )
+            {
+                prev_san = cur_san;
+                cur_san = cur_san->next;
+            }
+
+            cur = cur->next;
+        }/* while( cur != NULL ) */
+    }/* crt->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME */
+    return( 0 );
+}
+
 #define PRINT_ITEM(i)                           \
     {                                           \
         ret = mbedtls_snprintf( p, n, "%s" i, sep );    \
@@ -1659,11 +1915,12 @@
 
     if( crt->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME )
     {
-        ret = mbedtls_snprintf( p, n, "\n%ssubject alt name  : ", prefix );
+        ret = mbedtls_snprintf( p, n, "\n%ssubject alt name  :", prefix );
         MBEDTLS_X509_SAFE_SNPRINTF;
 
         if( ( ret = x509_info_subject_alt_name( &p, &n,
-                                            &crt->subject_alt_names ) ) != 0 )
+                                                &crt->subject_alt_names,
+                                                prefix) ) != 0 )
             return( ret );
     }