/* * Time ::= CHOICE { * utcTime UTCTime, * generalTime GeneralizedTime } */ int x509_get_time( unsigned char **p, const unsigned char *end, x509_time *time ) { int ret; size_t len; unsigned char tag; if( ( end - *p ) < 1 ) return( POLARSSL_ERR_X509_INVALID_DATE + POLARSSL_ERR_ASN1_OUT_OF_DATA ); tag = **p; if( tag == ASN1_UTC_TIME ) { (*p)++; ret = asn1_get_len( p, end, &len ); if( ret != 0 ) return( POLARSSL_ERR_X509_INVALID_DATE + ret ); CHECK( x509_parse_int( p, 2, &time->year ) ); CHECK( x509_parse_int( p, 2, &time->mon ) ); CHECK( x509_parse_int( p, 2, &time->day ) ); CHECK( x509_parse_int( p, 2, &time->hour ) ); CHECK( x509_parse_int( p, 2, &time->min ) ); if( len > 10 ) CHECK( x509_parse_int( p, 2, &time->sec ) ); if( len > 12 && *(*p)++ != 'Z' ) return( POLARSSL_ERR_X509_INVALID_DATE ); time->year += 100 * ( time->year < 50 ); time->year += 1900; return( 0 ); } else if( tag == ASN1_GENERALIZED_TIME ) { (*p)++; ret = asn1_get_len( p, end, &len ); if( ret != 0 ) return( POLARSSL_ERR_X509_INVALID_DATE + ret ); CHECK( x509_parse_int( p, 4, &time->year ) ); CHECK( x509_parse_int( p, 2, &time->mon ) ); CHECK( x509_parse_int( p, 2, &time->day ) ); CHECK( x509_parse_int( p, 2, &time->hour ) ); CHECK( x509_parse_int( p, 2, &time->min ) ); if( len > 12 ) CHECK( x509_parse_int( p, 2, &time->sec ) ); if( len > 14 && *(*p)++ != 'Z' ) return( POLARSSL_ERR_X509_INVALID_DATE ); return( 0 ); } else return( POLARSSL_ERR_X509_INVALID_DATE + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); }
static ssh_string asn1_get_int(ssh_buffer buffer) { ssh_string str; unsigned char type; uint32_t size; if (ssh_buffer_get_data(buffer, &type, 1) == 0 || type != ASN1_INTEGER) { return NULL; } size = asn1_get_len(buffer); if (size == 0) { return NULL; } str = ssh_string_new(size); if (str == NULL) { return NULL; } if (ssh_buffer_get_data(buffer, ssh_string_data(str), size) == 0) { ssh_string_free(str); return NULL; } return str; }
/* * CertificateSerialNumber ::= INTEGER */ static int x509_get_serial( unsigned char **p, unsigned char *end, x509_buf *serial ) { int ret; if( ( end - *p ) < 1 ) return( ERR_X509_CERT_INVALID_SERIAL | ERR_ASN1_OUT_OF_DATA ); if( **p != ( ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2 ) && **p != ASN1_INTEGER ) return( ERR_X509_CERT_INVALID_SERIAL | ERR_ASN1_UNEXPECTED_TAG ); serial->tag = *(*p)++; if( ( ret = asn1_get_len( p, end, &serial->len ) ) != 0 ) return( ERR_X509_CERT_INVALID_SERIAL | ret ); serial->p = *p; *p += serial->len; return( 0 ); }
static int asn1_check_sequence(ssh_buffer buffer) { unsigned char *j = NULL; unsigned char tmp; int i; uint32_t size; uint32_t padding; if (ssh_buffer_get_data(buffer, &tmp, 1) == 0 || tmp != ASN1_SEQUENCE) { return 0; } size = asn1_get_len(buffer); if ((padding = ssh_buffer_get_len(buffer) - size) > 0) { for (i = ssh_buffer_get_len(buffer) - size, j = (unsigned char*)ssh_buffer_get(buffer) + size; i; i--, j++) { if (*j != padding) { /* padding is allowed */ return 0; /* but nothing else */ } } } return 1; }
/* * CertificateSerialNumber ::= INTEGER */ static svn_error_t * x509_get_serial(const unsigned char **p, const unsigned char *end, x509_buf * serial) { svn_error_t *err; if ((end - *p) < 1) { err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL); } if (**p != (ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2) && **p != ASN1_INTEGER) { err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL); return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL); } serial->tag = *(*p)++; err = asn1_get_len(p, end, &serial->len); if (err) return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL); serial->p = *p; *p += serial->len; return SVN_NO_ERROR; }
/* * AttributeTypeAndValue ::= SEQUENCE { * type AttributeType, * value AttributeValue } * * AttributeType ::= OBJECT IDENTIFIER * * AttributeValue ::= ANY DEFINED BY AttributeType */ static svn_error_t * x509_get_attribute(const unsigned char **p, const unsigned char *end, x509_name *cur, apr_pool_t *result_pool) { svn_error_t *err; ptrdiff_t len; x509_buf *oid; x509_buf *val; err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); if (err) return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); end = *p + len; oid = &cur->oid; err = asn1_get_tag(p, end, &oid->len, ASN1_OID); if (err) return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); oid->tag = ASN1_OID; oid->p = *p; *p += oid->len; if ((end - *p) < 1) { err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); } if (**p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING && **p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING && **p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING) { err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL); return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); } val = &cur->val; val->tag = *(*p)++; err = asn1_get_len(p, end, &val->len); if (err) return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); val->p = *p; *p += val->len; cur->next = NULL; if (*p != end) { err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); } return SVN_NO_ERROR; }
/* * AttributeTypeAndValue ::= SEQUENCE { * type AttributeType, * value AttributeValue } * * AttributeType ::= OBJECT IDENTIFIER * * AttributeValue ::= ANY DEFINED BY AttributeType */ static int x509_get_attr_type_value( unsigned char **p, const unsigned char *end, x509_name *cur ) { int ret; size_t len; x509_buf *oid; x509_buf *val; if( ( ret = asn1_get_tag( p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) return( POLARSSL_ERR_X509_INVALID_NAME + ret ); if( ( end - *p ) < 1 ) return( POLARSSL_ERR_X509_INVALID_NAME + POLARSSL_ERR_ASN1_OUT_OF_DATA ); oid = &cur->oid; oid->tag = **p; if( ( ret = asn1_get_tag( p, end, &oid->len, ASN1_OID ) ) != 0 ) return( POLARSSL_ERR_X509_INVALID_NAME + ret ); oid->p = *p; *p += oid->len; if( ( end - *p ) < 1 ) return( POLARSSL_ERR_X509_INVALID_NAME + POLARSSL_ERR_ASN1_OUT_OF_DATA ); if( **p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING && **p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING && **p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING && **p != ASN1_BIT_STRING ) return( POLARSSL_ERR_X509_INVALID_NAME + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); val = &cur->val; val->tag = *(*p)++; if( ( ret = asn1_get_len( p, end, &val->len ) ) != 0 ) return( POLARSSL_ERR_X509_INVALID_NAME + ret ); val->p = *p; *p += val->len; cur->next = NULL; return( 0 ); }
static svn_error_t * asn1_get_tag(const unsigned char **p, const unsigned char *end, ptrdiff_t *len, int tag) { if ((end - *p) < 1) return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); if (**p != tag) return svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL); (*p)++; return svn_error_trace(asn1_get_len(p, end, len)); }
static int asn1_get_tag( unsigned char **p, unsigned char *end, int *len, int tag ) { if( ( end - *p ) < 1 ) return( ERR_ASN1_OUT_OF_DATA ); if( **p != tag ) return( ERR_ASN1_UNEXPECTED_TAG ); (*p)++; return( asn1_get_len( p, end, len ) ); }
int asn1_get_tag( unsigned char **p, const unsigned char *end, size_t *len, int tag ) { if( ( end - *p ) < 1 ) return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); if( **p != tag ) return( POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); (*p)++; return( asn1_get_len( p, end, len ) ); }
SSL_ROM_TEXT_SECTION int asn1_get_alg( unsigned char **p, const unsigned char *end, asn1_buf *alg, asn1_buf *params ) { int ret; size_t len; if( ( ret = asn1_get_tag( p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) return( ret ); if( ( end - *p ) < 1 ) return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); alg->tag = **p; end = *p + len; if( ( ret = asn1_get_tag( p, end, &alg->len, ASN1_OID ) ) != 0 ) return( ret ); alg->p = *p; *p += alg->len; if( *p == end ) { memset( params, 0, sizeof(asn1_buf) ); return( 0 ); } params->tag = **p; (*p)++; if( ( ret = asn1_get_len( p, end, ¶ms->len ) ) != 0 ) return( ret ); params->p = *p; *p += params->len; if( *p != end ) return( POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); return( 0 ); }
static gpg_error_t asn1_get_public_exp (unsigned char *cert, int cert_len, unsigned char **sub_start, int *sub_len) { gpg_error_t err; int len; /* The path to the public exp entry in the DER file. This is Sequence->Sequence->Version,Serial,AlgID,Issuer,Time,Subject, Sequence->Sequence,Bitstring->Sequence->Integer,Integer */ struct asn1_path path[] = { { '\x30', true }, { '\x30', true }, { '\xa0', false }, { '\x02', false }, { '\x30', false }, { '\x30', false }, { '\x30', false }, { '\x30', false }, { '\x30', true }, { '\x30', false }, { '\x03', true }, { '\x30', true }, { '\x02', false }, { '\x02', false } }; err = asn1_get_element (cert, cert_len, sub_start, sub_len, path, DIM (path)); if (err) return err; if (*sub_len < 1) { DEBUG (DBG_INFO, "public exponent too short"); return gpg_error (GPG_ERR_GENERAL); } (*sub_start)++; (*sub_len)--; err = asn1_get_len (sub_start, sub_len, &len); if (err) return err; /* PKCS #11 expects an unsigned big integer. */ while (**sub_start == '\x00' && *sub_len > 0) { (*sub_start)++; (*sub_len)--; } return 0; }
/* * SubjectAltName ::= GeneralNames * * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName * * GeneralName ::= CHOICE { * otherName [0] OtherName, * rfc822Name [1] IA5String, * dNSName [2] IA5String, * x400Address [3] ORAddress, * directoryName [4] Name, * ediPartyName [5] EDIPartyName, * uniformResourceIdentifier [6] IA5String, * iPAddress [7] OCTET STRING, * registeredID [8] OBJECT IDENTIFIER } * * OtherName ::= SEQUENCE { * type-id OBJECT IDENTIFIER, * value [0] EXPLICIT ANY DEFINED BY type-id } * * EDIPartyName ::= SEQUENCE { * nameAssigner [0] DirectoryString OPTIONAL, * partyName [1] DirectoryString } * * NOTE: PolarSSL only parses and uses dNSName at this point. */ static int x509_get_subject_alt_name( unsigned char **p, const unsigned char *end, x509_sequence *subject_alt_name ) { int ret; size_t len, tag_len; asn1_buf *buf; unsigned char tag; asn1_sequence *cur = subject_alt_name; /* Get main sequence tag */ if( ( ret = asn1_get_tag( p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); if( *p + len != end ) return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); while( *p < end ) { if( ( end - *p ) < 1 ) return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + POLARSSL_ERR_ASN1_OUT_OF_DATA ); tag = **p; (*p)++; if( ( ret = asn1_get_len( p, end, &tag_len ) ) != 0 ) return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); if( ( tag & ASN1_CONTEXT_SPECIFIC ) != ASN1_CONTEXT_SPECIFIC ) return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); if( tag != ( ASN1_CONTEXT_SPECIFIC | 2 ) ) { *p += tag_len; continue; } buf = &(cur->buf); buf->tag = tag; buf->p = *p; buf->len = tag_len; *p += buf->len; /* Allocate and assign next pointer */ if (*p < end) { cur->next = (asn1_sequence *) polarssl_malloc( sizeof( asn1_sequence ) ); if( cur->next == NULL ) return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + POLARSSL_ERR_ASN1_MALLOC_FAILED ); memset( cur->next, 0, sizeof( asn1_sequence ) ); cur = cur->next; } } /* Set final sequence entry's next pointer to NULL */ cur->next = NULL; if( *p != end ) return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); return( 0 ); }
static gpg_error_t asn1_get_element (unsigned char *cert, int cert_len, unsigned char **sub_start, int *sub_len, struct asn1_path *path, int path_size) { gpg_error_t err; unsigned char *prev_certp = NULL; unsigned char *certp = cert; int cert_left = cert_len; int len; int i; for (i = 0; i < path_size; i++) { prev_certp = certp; if (cert_left < 1) { DEBUG (DBG_INFO, "unexpected end of certificate"); return gpg_error (GPG_ERR_GENERAL); } if (*certp != path[i].tag) { DEBUG (DBG_INFO, "wrong element in lookup path"); return gpg_error (GPG_ERR_GENERAL); } certp++; cert_left--; err = asn1_get_len (&certp, &cert_left, &len); if (err) return err; if (!path[i].enter) { if (cert_left < len) { DEBUG (DBG_INFO, "unexpected end of certificate"); return gpg_error (GPG_ERR_GENERAL); } certp += len; cert_left -= len; } else { /* Special code to deal with ASN.1 data encapsulated in a bit string. */ if (path[i].tag == '\x03') { if (cert_left < 1) { DEBUG (DBG_INFO, "unexpected end of certificate"); return gpg_error (GPG_ERR_GENERAL); } if (*certp != '\x00') { DEBUG (DBG_INFO, "expected binary encapsulation missing"); return gpg_error (GPG_ERR_GENERAL); } certp++; cert_left--; } } } /* We found the subject. */ *sub_start = prev_certp; *sub_len = certp - prev_certp; return 0; }
/* * X.509 v3 extensions (not parsed) */ static svn_error_t * x509_get_ext(apr_array_header_t *dnsnames, const unsigned char **p, const unsigned char *end) { svn_error_t *err; ptrdiff_t len; if (*p == end) return SVN_NO_ERROR; err = asn1_get_tag(p, end, &len, ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3); if (err) { /* If there aren't extensions that's ok they aren't required */ if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG) { svn_error_clear(err); return SVN_NO_ERROR; } return svn_error_trace(err); } end = *p + len; SVN_ERR(asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE)); if (end != *p + len) { err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL); } while (*p < end) { ptrdiff_t ext_len; const unsigned char *ext_start, *sna_end; err = asn1_get_tag(p, end, &ext_len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); if (err) return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL); ext_start = *p; err = asn1_get_tag(p, end, &len, ASN1_OID); if (err) return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL); /* skip all extensions except SubjectAltName */ if (!equal(*p, len, OID_SUBJECT_ALT_NAME, sizeof(OID_SUBJECT_ALT_NAME) - 1)) { *p += ext_len - (*p - ext_start); continue; } *p += len; err = asn1_get_tag(p, end, &len, ASN1_OCTET_STRING); if (err) return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL); /* SubjectAltName ::= GeneralNames GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName GeneralName ::= CHOICE { other Name [0] OtherName, rfc822Name [1] IA5String, dNSName [2] IA5String, x400Address [3] ORAddress, directoryName [4] Name, ediPartyName [5] EDIPartyName, uniformResourceIdentifier [6] IA5String, iPAddress [7] OCTET STRING, registeredID [8] OBJECT IDENTIFIER } */ sna_end = *p + len; err = asn1_get_tag(p, sna_end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); if (err) return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL); if (sna_end != *p + len) { err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL); } while (*p < sna_end) { err = asn1_get_tag(p, sna_end, &len, ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2); if (err) { /* not not a dNSName */ if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG) { svn_error_clear(err); /* need to skip the tag and then find the length to * skip to ignore this SNA entry. */ (*p)++; SVN_ERR(asn1_get_len(p, sna_end, &len)); *p += len; continue; } return svn_error_trace(err); } else { /* We found a dNSName entry */ x509_buf *dnsname = apr_palloc(dnsnames->pool, sizeof(*dnsname)); dnsname->tag = ASN1_IA5_STRING; /* implicit based on dNSName */ dnsname->len = len; dnsname->p = *p; APR_ARRAY_PUSH(dnsnames, x509_buf *) = dnsname; } *p += len; } } return SVN_NO_ERROR; }
/* * RelativeDistinguishedName ::= * SET OF AttributeTypeAndValue * * AttributeTypeAndValue ::= SEQUENCE { * type AttributeType, * value AttributeValue } * * AttributeType ::= OBJECT IDENTIFIER * * AttributeValue ::= ANY DEFINED BY AttributeType */ static int x509_get_name( unsigned char **p, unsigned char *end, x509_name *cur ) { int ret, len; unsigned char *end2; x509_buf *oid; x509_buf *val; if( ( ret = asn1_get_tag( p, end, &len, ASN1_CONSTRUCTED | ASN1_SET ) ) != 0 ) return( ERR_X509_CERT_INVALID_NAME | ret ); end2 = end; end = *p + len; if( ( ret = asn1_get_tag( p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) return( ERR_X509_CERT_INVALID_NAME | ret ); if( *p + len != end ) return( ERR_X509_CERT_INVALID_NAME | ERR_ASN1_LENGTH_MISMATCH ); oid = &cur->oid; oid->tag = **p; if( ( ret = asn1_get_tag( p, end, &oid->len, ASN1_OID ) ) != 0 ) return( ERR_X509_CERT_INVALID_NAME | ret ); oid->p = *p; *p += oid->len; if( ( end - *p ) < 1 ) return( ERR_X509_CERT_INVALID_NAME | ERR_ASN1_OUT_OF_DATA ); if( **p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING && **p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING && **p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING ) return( ERR_X509_CERT_INVALID_NAME | ERR_ASN1_UNEXPECTED_TAG ); val = &cur->val; val->tag = *(*p)++; if( ( ret = asn1_get_len( p, end, &val->len ) ) != 0 ) return( ERR_X509_CERT_INVALID_NAME | ret ); val->p = *p; *p += val->len; cur->next = NULL; if( *p != end ) return( ERR_X509_CERT_INVALID_NAME | ERR_ASN1_LENGTH_MISMATCH ); /* * recurse until end of SEQUENCE is reached */ if( *p == end2 ) return( 0 ); cur->next = (x509_name *) malloc( sizeof( x509_name ) ); if( cur->next == NULL ) return( 1 ); return( x509_get_name( p, end2, cur->next ) ); }
/* * Time ::= CHOICE { * utcTime UTCTime, * generalTime GeneralizedTime } */ int x509_get_time( unsigned char **p, const unsigned char *end, x509_time *time ) { int ret; size_t len; char date[64]; unsigned char tag; if( ( end - *p ) < 1 ) return( POLARSSL_ERR_X509_INVALID_DATE + POLARSSL_ERR_ASN1_OUT_OF_DATA ); tag = **p; if( tag == ASN1_UTC_TIME ) { (*p)++; ret = asn1_get_len( p, end, &len ); if( ret != 0 ) return( POLARSSL_ERR_X509_INVALID_DATE + ret ); memset( date, 0, sizeof( date ) ); memcpy( date, *p, ( len < sizeof( date ) - 1 ) ? len : sizeof( date ) - 1 ); if( sscanf( date, "%2d%2d%2d%2d%2d%2dZ", &time->year, &time->mon, &time->day, &time->hour, &time->min, &time->sec ) < 5 ) return( POLARSSL_ERR_X509_INVALID_DATE ); time->year += 100 * ( time->year < 50 ); time->year += 1900; *p += len; return( 0 ); } else if( tag == ASN1_GENERALIZED_TIME ) { (*p)++; ret = asn1_get_len( p, end, &len ); if( ret != 0 ) return( POLARSSL_ERR_X509_INVALID_DATE + ret ); memset( date, 0, sizeof( date ) ); memcpy( date, *p, ( len < sizeof( date ) - 1 ) ? len : sizeof( date ) - 1 ); if( sscanf( date, "%4d%2d%2d%2d%2d%2dZ", &time->year, &time->mon, &time->day, &time->hour, &time->min, &time->sec ) < 5 ) return( POLARSSL_ERR_X509_INVALID_DATE ); *p += len; return( 0 ); } else return( POLARSSL_ERR_X509_INVALID_DATE + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); }