mbedtls/library/x509parse.c

3394 lines
83 KiB
C
Raw Normal View History

/*
* X.509 certificate and private key decoding
*
* Copyright (C) 2006-2013, Brainspark B.V.
2010-07-18 22:36:00 +02:00
*
* This file is part of PolarSSL (http://www.polarssl.org)
2010-07-18 12:13:04 +02:00
* Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
2010-07-18 22:36:00 +02:00
*
* All rights reserved.
2009-01-04 17:27:10 +01:00
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
2012-02-16 16:28:14 +01:00
* The ITU-T X.509 standard defines a certificate format for PKI.
*
* http://www.ietf.org/rfc/rfc3279.txt
2012-02-16 16:28:14 +01:00
* http://www.ietf.org/rfc/rfc3280.txt
*
* ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc
*
* http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf
* http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
*/
#include "polarssl/config.h"
#if defined(POLARSSL_X509_PARSE_C)
#include "polarssl/x509.h"
#include "polarssl/asn1.h"
#include "polarssl/oid.h"
#if defined(POLARSSL_PEM_PARSE_C)
#include "polarssl/pem.h"
#endif
#if defined(POLARSSL_PKCS5_C)
#include "polarssl/pkcs5.h"
#endif
#if defined(POLARSSL_PKCS12_C)
#include "polarssl/pkcs12.h"
#endif
#if defined(POLARSSL_MEMORY_C)
#include "polarssl/memory.h"
#else
#define polarssl_malloc malloc
#define polarssl_free free
#endif
#include <string.h>
#include <stdlib.h>
#if defined(_WIN32)
#include <windows.h>
#else
#include <time.h>
#endif
#if defined(POLARSSL_FS_IO)
#include <stdio.h>
#if !defined(_WIN32)
2012-06-04 14:46:42 +02:00
#include <sys/types.h>
#include <sys/stat.h>
2012-06-04 14:46:42 +02:00
#include <dirent.h>
#endif
#endif
/*
* Version ::= INTEGER { v1(0), v2(1), v3(2) }
*/
static int x509_get_version( unsigned char **p,
const unsigned char *end,
int *ver )
{
int ret;
size_t len;
if( ( ret = asn1_get_tag( p, end, &len,
ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0 ) ) != 0 )
{
if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG )
2011-10-19 16:15:17 +02:00
{
*ver = 0;
return( 0 );
}
return( ret );
}
end = *p + len;
if( ( ret = asn1_get_int( p, end, ver ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_VERSION + ret );
if( *p != end )
return( POLARSSL_ERR_X509_CERT_INVALID_VERSION +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
return( 0 );
}
/*
* Version ::= INTEGER { v1(0), v2(1) }
*/
static int x509_crl_get_version( unsigned char **p,
const unsigned char *end,
int *ver )
{
int ret;
if( ( ret = asn1_get_int( p, end, ver ) ) != 0 )
{
if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG )
2011-10-19 16:15:17 +02:00
{
*ver = 0;
return( 0 );
}
return( POLARSSL_ERR_X509_CERT_INVALID_VERSION + ret );
}
return( 0 );
}
/*
* Version ::= INTEGER { v1(0) }
*/
static int x509_csr_get_version( unsigned char **p,
const unsigned char *end,
int *ver )
{
int ret;
if( ( ret = asn1_get_int( p, end, ver ) ) != 0 )
{
if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG )
{
*ver = 0;
return( 0 );
}
return( POLARSSL_ERR_X509_CERT_INVALID_VERSION + ret );
}
return( 0 );
}
/*
* CertificateSerialNumber ::= INTEGER
*/
static int x509_get_serial( unsigned char **p,
const unsigned char *end,
x509_buf *serial )
{
int ret;
if( ( end - *p ) < 1 )
return( POLARSSL_ERR_X509_CERT_INVALID_SERIAL +
POLARSSL_ERR_ASN1_OUT_OF_DATA );
if( **p != ( ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2 ) &&
**p != ASN1_INTEGER )
return( POLARSSL_ERR_X509_CERT_INVALID_SERIAL +
POLARSSL_ERR_ASN1_UNEXPECTED_TAG );
serial->tag = *(*p)++;
if( ( ret = asn1_get_len( p, end, &serial->len ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_SERIAL + ret );
serial->p = *p;
*p += serial->len;
return( 0 );
}
/* Get an algorithm identifier without parameters (eg for signatures)
*
* AlgorithmIdentifier ::= SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL }
*/
static int x509_get_alg_null( unsigned char **p, const unsigned char *end,
x509_buf *alg )
{
int ret;
if( ( ret = asn1_get_alg_null( p, end, alg ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_ALG + ret );
return( 0 );
}
/*
* 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_CERT_INVALID_NAME + ret );
if( ( end - *p ) < 1 )
return( POLARSSL_ERR_X509_CERT_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_CERT_INVALID_NAME + ret );
oid->p = *p;
*p += oid->len;
if( ( end - *p ) < 1 )
return( POLARSSL_ERR_X509_CERT_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 )
return( POLARSSL_ERR_X509_CERT_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_CERT_INVALID_NAME + ret );
val->p = *p;
*p += val->len;
cur->next = NULL;
return( 0 );
}
/*
* 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,
const unsigned char *end,
x509_name *cur )
{
int ret;
size_t len;
const unsigned char *end2;
x509_name *use;
if( ( ret = asn1_get_tag( p, end, &len,
ASN1_CONSTRUCTED | ASN1_SET ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_NAME + ret );
end2 = end;
end = *p + len;
use = cur;
do
{
if( ( ret = x509_get_attr_type_value( p, end, use ) ) != 0 )
return( ret );
if( *p != end )
{
use->next = (x509_name *) polarssl_malloc(
sizeof( x509_name ) );
if( use->next == NULL )
return( POLARSSL_ERR_X509_MALLOC_FAILED );
memset( use->next, 0, sizeof( x509_name ) );
use = use->next;
}
}
while( *p != end );
/*
* recurse until end of SEQUENCE is reached
*/
if( *p == end2 )
return( 0 );
cur->next = (x509_name *) polarssl_malloc(
sizeof( x509_name ) );
if( cur->next == NULL )
return( POLARSSL_ERR_X509_MALLOC_FAILED );
memset( cur->next, 0, sizeof( x509_name ) );
return( x509_get_name( p, end2, cur->next ) );
}
/*
* Time ::= CHOICE {
* utcTime UTCTime,
* generalTime GeneralizedTime }
*/
static 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_CERT_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_CERT_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%2d",
&time->year, &time->mon, &time->day,
&time->hour, &time->min, &time->sec ) < 5 )
return( POLARSSL_ERR_X509_CERT_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_CERT_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%2d",
&time->year, &time->mon, &time->day,
&time->hour, &time->min, &time->sec ) < 5 )
return( POLARSSL_ERR_X509_CERT_INVALID_DATE );
*p += len;
return( 0 );
}
else
return( POLARSSL_ERR_X509_CERT_INVALID_DATE + POLARSSL_ERR_ASN1_UNEXPECTED_TAG );
}
/*
* Validity ::= SEQUENCE {
* notBefore Time,
* notAfter Time }
*/
static int x509_get_dates( unsigned char **p,
const unsigned char *end,
x509_time *from,
x509_time *to )
{
int ret;
size_t len;
if( ( ret = asn1_get_tag( p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_DATE + ret );
end = *p + len;
if( ( ret = x509_get_time( p, end, from ) ) != 0 )
return( ret );
if( ( ret = x509_get_time( p, end, to ) ) != 0 )
return( ret );
if( *p != end )
return( POLARSSL_ERR_X509_CERT_INVALID_DATE +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
return( 0 );
}
static int x509_get_sig( unsigned char **p,
const unsigned char *end,
x509_buf *sig )
{
int ret;
size_t len;
2012-02-11 19:42:45 +01:00
if( ( end - *p ) < 1 )
return( POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE +
POLARSSL_ERR_ASN1_OUT_OF_DATA );
sig->tag = **p;
if( ( ret = asn1_get_bitstring_null( p, end, &len ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE + ret );
sig->len = len;
sig->p = *p;
*p += len;
return( 0 );
}
/*
* X.509 v2/v3 unique identifier (not parsed)
*/
static int x509_get_uid( unsigned char **p,
const unsigned char *end,
x509_buf *uid, int n )
{
int ret;
if( *p == end )
return( 0 );
uid->tag = **p;
if( ( ret = asn1_get_tag( p, end, &uid->len,
ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n ) ) != 0 )
{
if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG )
return( 0 );
return( ret );
}
uid->p = *p;
*p += uid->len;
return( 0 );
}
/*
* X.509 Extensions (No parsing of extensions, pointer should
* be either manually updated or extensions should be parsed!
*/
static int x509_get_ext( unsigned char **p,
const unsigned char *end,
x509_buf *ext, int tag )
{
int ret;
size_t len;
if( *p == end )
return( 0 );
ext->tag = **p;
if( ( ret = asn1_get_tag( p, end, &ext->len,
ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | tag ) ) != 0 )
return( ret );
ext->p = *p;
end = *p + ext->len;
/*
* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
*
* Extension ::= SEQUENCE {
* extnID OBJECT IDENTIFIER,
* critical BOOLEAN DEFAULT FALSE,
* extnValue OCTET STRING }
*/
if( ( ret = asn1_get_tag( p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
if( end != *p + len )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
return( 0 );
}
/*
* X.509 CRL v2 extensions (no extensions parsed yet.)
*/
static int x509_get_crl_ext( unsigned char **p,
const unsigned char *end,
x509_buf *ext )
{
int ret;
size_t len = 0;
/* Get explicit tag */
if( ( ret = x509_get_ext( p, end, ext, 0) ) != 0 )
{
if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG )
return( 0 );
return( ret );
}
while( *p < end )
{
if( ( ret = asn1_get_tag( p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
*p += len;
}
if( *p != end )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
return( 0 );
}
/*
* X.509 CRL v2 entry extensions (no extensions parsed yet.)
*/
static int x509_get_crl_entry_ext( unsigned char **p,
const unsigned char *end,
x509_buf *ext )
{
int ret;
size_t len = 0;
/* OPTIONAL */
if (end <= *p)
return( 0 );
ext->tag = **p;
ext->p = *p;
/*
* Get CRL-entry extension sequence header
* crlEntryExtensions Extensions OPTIONAL -- if present, MUST be v2
*/
if( ( ret = asn1_get_tag( p, end, &ext->len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
{
if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG )
{
ext->p = NULL;
return( 0 );
}
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
}
end = *p + ext->len;
if( end != *p + ext->len )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
while( *p < end )
{
if( ( ret = asn1_get_tag( p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
*p += len;
}
if( *p != end )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
return( 0 );
}
static int x509_get_basic_constraints( unsigned char **p,
const unsigned char *end,
int *ca_istrue,
int *max_pathlen )
{
int ret;
size_t len;
/*
* BasicConstraints ::= SEQUENCE {
* cA BOOLEAN DEFAULT FALSE,
* pathLenConstraint INTEGER (0..MAX) OPTIONAL }
*/
*ca_istrue = 0; /* DEFAULT FALSE */
*max_pathlen = 0; /* endless */
if( ( ret = asn1_get_tag( p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
if( *p == end )
return 0;
if( ( ret = asn1_get_bool( p, end, ca_istrue ) ) != 0 )
{
if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG )
ret = asn1_get_int( p, end, ca_istrue );
if( ret != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
if( *ca_istrue != 0 )
*ca_istrue = 1;
}
if( *p == end )
return 0;
if( ( ret = asn1_get_int( p, end, max_pathlen ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
if( *p != end )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
(*max_pathlen)++;
return 0;
}
static int x509_get_ns_cert_type( unsigned char **p,
const unsigned char *end,
unsigned char *ns_cert_type)
{
int ret;
2011-01-18 17:17:47 +01:00
x509_bitstring bs = { 0, 0, NULL };
if( ( ret = asn1_get_bitstring( p, end, &bs ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
if( bs.len != 1 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
POLARSSL_ERR_ASN1_INVALID_LENGTH );
/* Get actual bitstring */
*ns_cert_type = *bs.p;
return 0;
}
static int x509_get_key_usage( unsigned char **p,
const unsigned char *end,
unsigned char *key_usage)
{
int ret;
2011-01-18 17:17:47 +01:00
x509_bitstring bs = { 0, 0, NULL };
if( ( ret = asn1_get_bitstring( p, end, &bs ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
if( bs.len < 1 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
POLARSSL_ERR_ASN1_INVALID_LENGTH );
/* Get actual bitstring */
*key_usage = *bs.p;
return 0;
}
/*
* ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
*
* KeyPurposeId ::= OBJECT IDENTIFIER
*/
static int x509_get_ext_key_usage( unsigned char **p,
const unsigned char *end,
x509_sequence *ext_key_usage)
{
int ret;
if( ( ret = asn1_get_sequence_of( p, end, ext_key_usage, ASN1_OID ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
/* Sequence length must be >= 1 */
if( ext_key_usage->buf.p == NULL )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
POLARSSL_ERR_ASN1_INVALID_LENGTH );
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_CERT_INVALID_EXTENSIONS + ret );
if( *p + len != end )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
while( *p < end )
{
if( ( end - *p ) < 1 )
return( POLARSSL_ERR_X509_CERT_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_CERT_INVALID_EXTENSIONS + ret );
if( ( tag & ASN1_CONTEXT_SPECIFIC ) != ASN1_CONTEXT_SPECIFIC )
return( POLARSSL_ERR_X509_CERT_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_CERT_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_CERT_INVALID_EXTENSIONS +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
return( 0 );
}
/*
* X.509 v3 extensions
*
* TODO: Perform all of the basic constraints tests required by the RFC
* TODO: Set values for undetected extensions to a sane default?
*
*/
static int x509_get_crt_ext( unsigned char **p,
const unsigned char *end,
x509_cert *crt )
{
int ret;
size_t len;
2009-07-27 23:34:45 +02:00
unsigned char *end_ext_data, *end_ext_octet;
if( ( ret = x509_get_ext( p, end, &crt->v3_ext, 3 ) ) != 0 )
{
if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG )
return( 0 );
return( ret );
}
while( *p < end )
{
/*
* Extension ::= SEQUENCE {
* extnID OBJECT IDENTIFIER,
* critical BOOLEAN DEFAULT FALSE,
* extnValue OCTET STRING }
*/
x509_buf extn_oid = {0, 0, NULL};
int is_critical = 0; /* DEFAULT FALSE */
int ext_type = 0;
if( ( ret = asn1_get_tag( p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
2009-07-27 23:34:45 +02:00
end_ext_data = *p + len;
/* Get extension ID */
extn_oid.tag = **p;
if( ( ret = asn1_get_tag( p, end, &extn_oid.len, ASN1_OID ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
extn_oid.p = *p;
*p += extn_oid.len;
if( ( end - *p ) < 1 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
POLARSSL_ERR_ASN1_OUT_OF_DATA );
/* Get optional critical */
2009-07-27 23:34:45 +02:00
if( ( ret = asn1_get_bool( p, end_ext_data, &is_critical ) ) != 0 &&
( ret != POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
/* Data should be octet string type */
2009-07-27 23:34:45 +02:00
if( ( ret = asn1_get_tag( p, end_ext_data, &len,
ASN1_OCTET_STRING ) ) != 0 )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
2009-07-27 23:34:45 +02:00
end_ext_octet = *p + len;
2009-07-27 23:34:45 +02:00
if( end_ext_octet != end_ext_data )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
2009-07-27 23:34:45 +02:00
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
/*
* Detect supported extensions
*/
ret = oid_get_x509_ext_type( &extn_oid, &ext_type );
if( ret != 0 )
{
/* No parser found, skip extension */
*p = end_ext_octet;
#if !defined(POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION)
if( is_critical )
{
/* Data is marked as critical: fail */
return ( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
POLARSSL_ERR_ASN1_UNEXPECTED_TAG );
}
#endif
continue;
}
crt->ext_types |= ext_type;
switch( ext_type )
{
case EXT_BASIC_CONSTRAINTS:
/* Parse basic constraints */
if( ( ret = x509_get_basic_constraints( p, end_ext_octet,
&crt->ca_istrue, &crt->max_pathlen ) ) != 0 )
return ( ret );
break;
case EXT_KEY_USAGE:
/* Parse key usage */
if( ( ret = x509_get_key_usage( p, end_ext_octet,
&crt->key_usage ) ) != 0 )
return ( ret );
break;
case EXT_EXTENDED_KEY_USAGE:
/* Parse extended key usage */
if( ( ret = x509_get_ext_key_usage( p, end_ext_octet,
&crt->ext_key_usage ) ) != 0 )
return ( ret );
break;
case EXT_SUBJECT_ALT_NAME:
/* Parse subject alt name */
if( ( ret = x509_get_subject_alt_name( p, end_ext_octet,
&crt->subject_alt_names ) ) != 0 )
return ( ret );
break;
case EXT_NS_CERT_TYPE:
/* Parse netscape certificate type */
if( ( ret = x509_get_ns_cert_type( p, end_ext_octet,
&crt->ns_cert_type ) ) != 0 )
return ( ret );
break;
default:
return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE );
}
}
if( *p != end )
return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
return( 0 );
}
/*
* X.509 CRL Entries
*/
static int x509_get_entries( unsigned char **p,
const unsigned char *end,
x509_crl_entry *entry )
{
int ret;
size_t entry_len;
x509_crl_entry *cur_entry = entry;
if( *p == end )
return( 0 );
if( ( ret = asn1_get_tag( p, end, &entry_len,
ASN1_SEQUENCE | ASN1_CONSTRUCTED ) ) != 0 )
{
if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG )
return( 0 );
return( ret );
}
end = *p + entry_len;
while( *p < end )
{
size_t len2;
const unsigned char *end2;
if( ( ret = asn1_get_tag( p, end, &len2,
ASN1_SEQUENCE | ASN1_CONSTRUCTED ) ) != 0 )
{
return( ret );
}
cur_entry->raw.tag = **p;
cur_entry->raw.p = *p;
cur_entry->raw.len = len2;
end2 = *p + len2;
if( ( ret = x509_get_serial( p, end2, &cur_entry->serial ) ) != 0 )
return( ret );
if( ( ret = x509_get_time( p, end2, &cur_entry->revocation_date ) ) != 0 )
return( ret );
if( ( ret = x509_get_crl_entry_ext( p, end2, &cur_entry->entry_ext ) ) != 0 )
return( ret );
if ( *p < end )
{
cur_entry->next = polarssl_malloc( sizeof( x509_crl_entry ) );
if( cur_entry->next == NULL )
return( POLARSSL_ERR_X509_MALLOC_FAILED );
cur_entry = cur_entry->next;
memset( cur_entry, 0, sizeof( x509_crl_entry ) );
}
}
return( 0 );
}
static int x509_get_sig_alg( const x509_buf *sig_oid, md_type_t *md_alg,
pk_type_t *pk_alg )
{
int ret = oid_get_sig_alg( sig_oid, md_alg, pk_alg );
if( ret != 0 )
return( POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG + ret );
return( 0 );
}
/*
* Parse and fill a single X.509 certificate in DER format
*/
static int x509parse_crt_der_core( x509_cert *crt, const unsigned char *buf,
size_t buflen )
{
int ret;
size_t len;
unsigned char *p, *end, *crt_end;
/*
* Check for valid input
*/
if( crt == NULL || buf == NULL )
return( POLARSSL_ERR_X509_INVALID_INPUT );
p = (unsigned char *) polarssl_malloc( len = buflen );
if( p == NULL )
return( POLARSSL_ERR_X509_MALLOC_FAILED );
memcpy( p, buf, buflen );
buflen = 0;
crt->raw.p = p;
crt->raw.len = len;
end = p + len;
/*
* Certificate ::= SEQUENCE {
* tbsCertificate TBSCertificate,
* signatureAlgorithm AlgorithmIdentifier,
* signatureValue BIT STRING }
*/
if( ( ret = asn1_get_tag( &p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT );
}
if( len > (size_t) ( end - p ) )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
}
crt_end = p + len;
/*
* TBSCertificate ::= SEQUENCE {
*/
crt->tbs.p = p;
if( ( ret = asn1_get_tag( &p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret );
}
end = p + len;
crt->tbs.len = end - crt->tbs.p;
/*
* Version ::= INTEGER { v1(0), v2(1), v3(2) }
*
* CertificateSerialNumber ::= INTEGER
*
* signature AlgorithmIdentifier
*/
if( ( ret = x509_get_version( &p, end, &crt->version ) ) != 0 ||
( ret = x509_get_serial( &p, end, &crt->serial ) ) != 0 ||
( ret = x509_get_alg_null( &p, end, &crt->sig_oid1 ) ) != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( ret );
}
crt->version++;
if( crt->version > 3 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( POLARSSL_ERR_X509_CERT_UNKNOWN_VERSION );
}
if( ( ret = x509_get_sig_alg( &crt->sig_oid1, &crt->sig_md,
&crt->sig_pk ) ) != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( ret );
}
/*
* issuer Name
*/
crt->issuer_raw.p = p;
if( ( ret = asn1_get_tag( &p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret );
}
if( ( ret = x509_get_name( &p, p + len, &crt->issuer ) ) != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( ret );
}
crt->issuer_raw.len = p - crt->issuer_raw.p;
/*
* Validity ::= SEQUENCE {
* notBefore Time,
* notAfter Time }
*
*/
if( ( ret = x509_get_dates( &p, end, &crt->valid_from,
&crt->valid_to ) ) != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( ret );
}
/*
* subject Name
*/
crt->subject_raw.p = p;
if( ( ret = asn1_get_tag( &p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret );
}
if( len && ( ret = x509_get_name( &p, p + len, &crt->subject ) ) != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( ret );
}
crt->subject_raw.len = p - crt->subject_raw.p;
/*
* SubjectPublicKeyInfo
*/
if( ( ret = pk_parse_get_pubkey( &p, end, &crt->pk ) ) != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( ret );
}
/*
* issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
* -- If present, version shall be v2 or v3
* subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
* -- If present, version shall be v2 or v3
* extensions [3] EXPLICIT Extensions OPTIONAL
* -- If present, version shall be v3
*/
if( crt->version == 2 || crt->version == 3 )
{
ret = x509_get_uid( &p, end, &crt->issuer_id, 1 );
if( ret != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( ret );
}
}
if( crt->version == 2 || crt->version == 3 )
{
ret = x509_get_uid( &p, end, &crt->subject_id, 2 );
if( ret != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( ret );
}
}
if( crt->version == 3 )
{
ret = x509_get_crt_ext( &p, end, crt);
if( ret != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( ret );
}
}
if( p != end )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
}
end = crt_end;
/*
* }
* -- end of TBSCertificate
*
* signatureAlgorithm AlgorithmIdentifier,
* signatureValue BIT STRING
*/
if( ( ret = x509_get_alg_null( &p, end, &crt->sig_oid2 ) ) != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( ret );
}
if( crt->sig_oid1.len != crt->sig_oid2.len ||
memcmp( crt->sig_oid1.p, crt->sig_oid2.p, crt->sig_oid1.len ) != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( POLARSSL_ERR_X509_CERT_SIG_MISMATCH );
}
if( ( ret = x509_get_sig( &p, end, &crt->sig ) ) != 0 )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( ret );
}
if( p != end )
{
2009-05-02 17:53:56 +02:00
x509_free( crt );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
}
return( 0 );
}
/*
* Parse one X.509 certificate in DER format from a buffer and add them to a
* chained list
*/
int x509parse_crt_der( x509_cert *chain, const unsigned char *buf, size_t buflen )
{
int ret;
x509_cert *crt = chain, *prev = NULL;
/*
* Check for valid input
*/
if( crt == NULL || buf == NULL )
return( POLARSSL_ERR_X509_INVALID_INPUT );
while( crt->version != 0 && crt->next != NULL )
{
prev = crt;
crt = crt->next;
}
/*
* Add new certificate on the end of the chain if needed.
*/
if ( crt->version != 0 && crt->next == NULL)
{
crt->next = (x509_cert *) polarssl_malloc( sizeof( x509_cert ) );
2009-05-02 17:53:56 +02:00
if( crt->next == NULL )
return( POLARSSL_ERR_X509_MALLOC_FAILED );
prev = crt;
2009-05-02 17:53:56 +02:00
crt = crt->next;
memset( crt, 0, sizeof( x509_cert ) );
}
if( ( ret = x509parse_crt_der_core( crt, buf, buflen ) ) != 0 )
{
if( prev )
prev->next = NULL;
if( crt != chain )
polarssl_free( crt );
return( ret );
}
return( 0 );
}
/*
* Parse one or more PEM certificates from a buffer and add them to the chained list
*/
int x509parse_crt( x509_cert *chain, const unsigned char *buf, size_t buflen )
{
int success = 0, first_error = 0, total_failed = 0;
int buf_format = X509_FORMAT_DER;
/*
* Check for valid input
*/
if( chain == NULL || buf == NULL )
return( POLARSSL_ERR_X509_INVALID_INPUT );
/*
* Determine buffer content. Buffer contains either one DER certificate or
* one or more PEM certificates.
*/
#if defined(POLARSSL_PEM_PARSE_C)
if( strstr( (const char *) buf, "-----BEGIN CERTIFICATE-----" ) != NULL )
buf_format = X509_FORMAT_PEM;
#endif
if( buf_format == X509_FORMAT_DER )
return x509parse_crt_der( chain, buf, buflen );
#if defined(POLARSSL_PEM_PARSE_C)
if( buf_format == X509_FORMAT_PEM )
{
int ret;
pem_context pem;
while( buflen > 0 )
{
size_t use_len;
pem_init( &pem );
ret = pem_read_buffer( &pem,
"-----BEGIN CERTIFICATE-----",
"-----END CERTIFICATE-----",
buf, NULL, 0, &use_len );
if( ret == 0 )
{
/*
* Was PEM encoded
*/
buflen -= use_len;
buf += use_len;
}
else if( ret == POLARSSL_ERR_PEM_BAD_INPUT_DATA )
{
return( ret );
}
else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT )
{
pem_free( &pem );
/*
* PEM header and footer were found
*/
buflen -= use_len;
buf += use_len;
if( first_error == 0 )
first_error = ret;
continue;
}
else
break;
ret = x509parse_crt_der( chain, pem.buf, pem.buflen );
pem_free( &pem );
if( ret != 0 )
{
/*
* Quit parsing on a memory error
*/
if( ret == POLARSSL_ERR_X509_MALLOC_FAILED )
return( ret );
if( first_error == 0 )
first_error = ret;
total_failed++;
continue;
}
success = 1;
}
}
#endif
if( success )
return( total_failed );
else if( first_error )
return( first_error );
else
return( POLARSSL_ERR_X509_CERT_UNKNOWN_FORMAT );
}
/*
* Parse a CSR
*/
int x509parse_csr( x509_csr *csr, const unsigned char *buf, size_t buflen )
{
int ret;
size_t len;
unsigned char *p, *end;
#if defined(POLARSSL_PEM_PARSE_C)
size_t use_len;
pem_context pem;
#endif
/*
* Check for valid input
*/
if( csr == NULL || buf == NULL )
return( POLARSSL_ERR_X509_INVALID_INPUT );
memset( csr, 0, sizeof( x509_csr ) );
#if defined(POLARSSL_PEM_PARSE_C)
pem_init( &pem );
ret = pem_read_buffer( &pem,
"-----BEGIN CERTIFICATE REQUEST-----",
"-----END CERTIFICATE REQUEST-----",
buf, NULL, 0, &use_len );
if( ret == 0 )
{
/*
* Was PEM encoded
*/
buflen -= use_len;
buf += use_len;
/*
* Steal PEM buffer
*/
p = pem.buf;
pem.buf = NULL;
len = pem.buflen;
pem_free( &pem );
}
else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT )
{
pem_free( &pem );
return( ret );
}
else
2013-09-10 15:59:02 +02:00
#endif
{
/*
* nope, copy the raw DER data
*/
p = (unsigned char *) polarssl_malloc( len = buflen );
if( p == NULL )
return( POLARSSL_ERR_X509_MALLOC_FAILED );
memcpy( p, buf, buflen );
buflen = 0;
}
csr->raw.p = p;
csr->raw.len = len;
end = p + len;
/*
* CertificationRequest ::= SEQUENCE {
* certificationRequestInfo CertificationRequestInfo,
* signatureAlgorithm AlgorithmIdentifier,
* signature BIT STRING
* }
*/
if( ( ret = asn1_get_tag( &p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
{
x509_csr_free( csr );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT );
}
if( len != (size_t) ( end - p ) )
{
x509_csr_free( csr );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
}
/*
* CertificationRequestInfo ::= SEQUENCE {
*/
csr->cri.p = p;
if( ( ret = asn1_get_tag( &p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
{
x509_csr_free( csr );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret );
}
end = p + len;
csr->cri.len = end - csr->cri.p;
/*
* Version ::= INTEGER { v1(0) }
*/
if( ( ret = x509_csr_get_version( &p, end, &csr->version ) ) != 0 )
{
x509_csr_free( csr );
return( ret );
}
csr->version++;
if( csr->version != 1 )
{
x509_csr_free( csr );
return( POLARSSL_ERR_X509_CERT_UNKNOWN_VERSION );
}
/*
* subject Name
*/
csr->subject_raw.p = p;
if( ( ret = asn1_get_tag( &p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
{
x509_csr_free( csr );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret );
}
if( ( ret = x509_get_name( &p, p + len, &csr->subject ) ) != 0 )
{
x509_csr_free( csr );
return( ret );
}
csr->subject_raw.len = p - csr->subject_raw.p;
/*
* subjectPKInfo SubjectPublicKeyInfo
*/
if( ( ret = pk_parse_get_pubkey( &p, end, &csr->pk ) ) != 0 )
{
x509_csr_free( csr );
return( ret );
}
/*
* attributes [0] Attributes
*/
if( ( ret = asn1_get_tag( &p, end, &len,
ASN1_CONSTRUCTED | ASN1_CONTEXT_SPECIFIC ) ) != 0 )
{
x509_csr_free( csr );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret );
}
// TODO Parse Attributes / extension requests
p += len;
end = csr->raw.p + csr->raw.len;
/*
* signatureAlgorithm AlgorithmIdentifier,
* signature BIT STRING
*/
if( ( ret = x509_get_alg_null( &p, end, &csr->sig_oid ) ) != 0 )
{
x509_csr_free( csr );
return( ret );
}
if( ( ret = x509_get_sig_alg( &csr->sig_oid, &csr->sig_md,
&csr->sig_pk ) ) != 0 )
{
x509_csr_free( csr );
return( POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG );
}
if( ( ret = x509_get_sig( &p, end, &csr->sig ) ) != 0 )
{
x509_csr_free( csr );
return( ret );
}
if( p != end )
{
x509_csr_free( csr );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
}
return( 0 );
}
/*
* Parse one or more CRLs and add them to the chained list
*/
int x509parse_crl( x509_crl *chain, const unsigned char *buf, size_t buflen )
{
int ret;
size_t len;
unsigned char *p, *end;
x509_crl *crl;
#if defined(POLARSSL_PEM_PARSE_C)
size_t use_len;
pem_context pem;
#endif
crl = chain;
/*
* Check for valid input
*/
if( crl == NULL || buf == NULL )
return( POLARSSL_ERR_X509_INVALID_INPUT );
while( crl->version != 0 && crl->next != NULL )
crl = crl->next;
/*
* Add new CRL on the end of the chain if needed.
*/
if ( crl->version != 0 && crl->next == NULL)
{
crl->next = (x509_crl *) polarssl_malloc( sizeof( x509_crl ) );
2009-05-02 17:53:56 +02:00
if( crl->next == NULL )
{
x509_crl_free( crl );
return( POLARSSL_ERR_X509_MALLOC_FAILED );
2009-05-02 17:53:56 +02:00
}
2009-05-02 17:53:56 +02:00
crl = crl->next;
memset( crl, 0, sizeof( x509_crl ) );
}
#if defined(POLARSSL_PEM_PARSE_C)
pem_init( &pem );
ret = pem_read_buffer( &pem,
"-----BEGIN X509 CRL-----",
"-----END X509 CRL-----",
buf, NULL, 0, &use_len );
if( ret == 0 )
{
/*
* Was PEM encoded
*/
buflen -= use_len;
buf += use_len;
/*
* Steal PEM buffer
*/
p = pem.buf;
pem.buf = NULL;
len = pem.buflen;
pem_free( &pem );
}
else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT )
{
pem_free( &pem );
return( ret );
}
else
2013-09-10 15:59:02 +02:00
#endif
{
/*
* nope, copy the raw DER data
*/
p = (unsigned char *) polarssl_malloc( len = buflen );
if( p == NULL )
return( POLARSSL_ERR_X509_MALLOC_FAILED );
memcpy( p, buf, buflen );
buflen = 0;
}
crl->raw.p = p;
crl->raw.len = len;
end = p + len;
/*
* CertificateList ::= SEQUENCE {
* tbsCertList TBSCertList,
* signatureAlgorithm AlgorithmIdentifier,
* signatureValue BIT STRING }
*/
if( ( ret = asn1_get_tag( &p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
{
x509_crl_free( crl );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT );
}
if( len != (size_t) ( end - p ) )
{
x509_crl_free( crl );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
}
/*
* TBSCertList ::= SEQUENCE {
*/
crl->tbs.p = p;
if( ( ret = asn1_get_tag( &p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
{
x509_crl_free( crl );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret );
}
end = p + len;
crl->tbs.len = end - crl->tbs.p;
/*
* Version ::= INTEGER OPTIONAL { v1(0), v2(1) }
* -- if present, MUST be v2
*
* signature AlgorithmIdentifier
*/
if( ( ret = x509_crl_get_version( &p, end, &crl->version ) ) != 0 ||
( ret = x509_get_alg_null( &p, end, &crl->sig_oid1 ) ) != 0 )
{
x509_crl_free( crl );
return( ret );
}
crl->version++;
if( crl->version > 2 )
{
x509_crl_free( crl );
return( POLARSSL_ERR_X509_CERT_UNKNOWN_VERSION );
}
if( ( ret = x509_get_sig_alg( &crl->sig_oid1, &crl->sig_md,
&crl->sig_pk ) ) != 0 )
{
x509_crl_free( crl );
return( POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG );
}
/*
* issuer Name
*/
crl->issuer_raw.p = p;
if( ( ret = asn1_get_tag( &p, end, &len,
ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
{
x509_crl_free( crl );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret );
}
if( ( ret = x509_get_name( &p, p + len, &crl->issuer ) ) != 0 )
{
x509_crl_free( crl );
return( ret );
}
crl->issuer_raw.len = p - crl->issuer_raw.p;
/*
* thisUpdate Time
* nextUpdate Time OPTIONAL
*/
if( ( ret = x509_get_time( &p, end, &crl->this_update ) ) != 0 )
{
x509_crl_free( crl );
return( ret );
}
if( ( ret = x509_get_time( &p, end, &crl->next_update ) ) != 0 )
{
if ( ret != ( POLARSSL_ERR_X509_CERT_INVALID_DATE +
POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) &&
ret != ( POLARSSL_ERR_X509_CERT_INVALID_DATE +
POLARSSL_ERR_ASN1_OUT_OF_DATA ) )
{
x509_crl_free( crl );
return( ret );
}
}
/*
* revokedCertificates SEQUENCE OF SEQUENCE {
* userCertificate CertificateSerialNumber,
* revocationDate Time,
* crlEntryExtensions Extensions OPTIONAL
* -- if present, MUST be v2
* } OPTIONAL
*/
if( ( ret = x509_get_entries( &p, end, &crl->entry ) ) != 0 )
{
x509_crl_free( crl );
return( ret );
}
/*
* crlExtensions EXPLICIT Extensions OPTIONAL
* -- if present, MUST be v2
*/
if( crl->version == 2 )
{
ret = x509_get_crl_ext( &p, end, &crl->crl_ext );
if( ret != 0 )
{
x509_crl_free( crl );
return( ret );
}
}
if( p != end )
{
x509_crl_free( crl );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
}
end = crl->raw.p + crl->raw.len;
/*
* signatureAlgorithm AlgorithmIdentifier,
* signatureValue BIT STRING
*/
if( ( ret = x509_get_alg_null( &p, end, &crl->sig_oid2 ) ) != 0 )
{
x509_crl_free( crl );
return( ret );
}
if( crl->sig_oid1.len != crl->sig_oid2.len ||
memcmp( crl->sig_oid1.p, crl->sig_oid2.p, crl->sig_oid1.len ) != 0 )
{
x509_crl_free( crl );
return( POLARSSL_ERR_X509_CERT_SIG_MISMATCH );
}
if( ( ret = x509_get_sig( &p, end, &crl->sig ) ) != 0 )
{
x509_crl_free( crl );
return( ret );
}
if( p != end )
{
x509_crl_free( crl );
return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT +
POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
}
if( buflen > 0 )
{
crl->next = (x509_crl *) polarssl_malloc( sizeof( x509_crl ) );
2009-05-02 17:53:56 +02:00
if( crl->next == NULL )
{
x509_crl_free( crl );
return( POLARSSL_ERR_X509_MALLOC_FAILED );
2009-05-02 17:53:56 +02:00
}
2009-05-02 17:53:56 +02:00
crl = crl->next;
memset( crl, 0, sizeof( x509_crl ) );
return( x509parse_crl( crl, buf, buflen ) );
}
return( 0 );
}
#if defined(POLARSSL_FS_IO)
2009-04-19 20:44:26 +02:00
/*
* Load all data from a file into a given buffer.
*/
static int load_file( const char *path, unsigned char **buf, size_t *n )
2009-04-19 20:44:26 +02:00
{
FILE *f;
long size;
2009-04-19 20:44:26 +02:00
if( ( f = fopen( path, "rb" ) ) == NULL )
return( POLARSSL_ERR_X509_FILE_IO_ERROR );
2009-04-19 20:44:26 +02:00
fseek( f, 0, SEEK_END );
if( ( size = ftell( f ) ) == -1 )
{
fclose( f );
return( POLARSSL_ERR_X509_FILE_IO_ERROR );
}
fseek( f, 0, SEEK_SET );
2009-04-19 20:44:26 +02:00
*n = (size_t) size;
if( *n + 1 == 0 ||
( *buf = (unsigned char *) polarssl_malloc( *n + 1 ) ) == NULL )
{
fclose( f );
return( POLARSSL_ERR_X509_MALLOC_FAILED );
}
2009-04-19 20:44:26 +02:00
if( fread( *buf, 1, *n, f ) != *n )
{
fclose( f );
polarssl_free( *buf );
return( POLARSSL_ERR_X509_FILE_IO_ERROR );
}
2009-04-19 20:44:26 +02:00
fclose( f );
2009-04-19 20:44:26 +02:00
(*buf)[*n] = '\0';
2009-04-19 20:44:26 +02:00
return( 0 );
2009-04-19 20:44:26 +02:00
}
/*
* Load one or more certificates and add them to the chained list
*/
int x509parse_crtfile( x509_cert *chain, const char *path )
{
int ret;
size_t n;
unsigned char *buf;
2013-06-27 13:00:00 +02:00
if ( ( ret = load_file( path, &buf, &n ) ) != 0 )
return( ret );
ret = x509parse_crt( chain, buf, n );
memset( buf, 0, n + 1 );
polarssl_free( buf );
return( ret );
}
2012-06-04 14:46:42 +02:00
int x509parse_crtpath( x509_cert *chain, const char *path )
{
int ret = 0;
#if defined(_WIN32)
int w_ret;
WCHAR szDir[MAX_PATH];
char filename[MAX_PATH];
char *p;
int len = strlen( path );
2012-11-02 13:53:26 +01:00
WIN32_FIND_DATAW file_data;
2012-06-04 14:46:42 +02:00
HANDLE hFind;
if( len > MAX_PATH - 3 )
return( POLARSSL_ERR_X509_INVALID_INPUT );
2012-06-04 14:46:42 +02:00
memset( szDir, 0, sizeof(szDir) );
memset( filename, 0, MAX_PATH );
memcpy( filename, path, len );
filename[len++] = '\\';
p = filename + len;
filename[len++] = '*';
w_ret = MultiByteToWideChar( CP_ACP, 0, path, len, szDir, MAX_PATH - 3 );
2012-06-04 14:46:42 +02:00
2012-11-02 13:53:26 +01:00
hFind = FindFirstFileW( szDir, &file_data );
2012-06-04 14:46:42 +02:00
if (hFind == INVALID_HANDLE_VALUE)
return( POLARSSL_ERR_X509_FILE_IO_ERROR );
len = MAX_PATH - len;
2012-06-04 14:46:42 +02:00
do
{
memset( p, 0, len );
if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
2012-06-04 14:46:42 +02:00
continue;
w_ret = WideCharToMultiByte( CP_ACP, 0, file_data.cFileName,
lstrlenW(file_data.cFileName),
p, len - 1,
NULL, NULL );
2012-06-04 14:46:42 +02:00
w_ret = x509parse_crtfile( chain, filename );
if( w_ret < 0 )
ret++;
else
ret += w_ret;
2012-06-04 14:46:42 +02:00
}
2012-11-02 13:53:26 +01:00
while( FindNextFileW( hFind, &file_data ) != 0 );
2012-06-04 14:46:42 +02:00
if (GetLastError() != ERROR_NO_MORE_FILES)
ret = POLARSSL_ERR_X509_FILE_IO_ERROR;
2012-06-04 14:46:42 +02:00
cleanup:
2012-06-04 14:46:42 +02:00
FindClose( hFind );
#else
int t_ret, i;
struct stat sb;
struct dirent entry, *result = NULL;
2012-06-04 14:46:42 +02:00
char entry_name[255];
DIR *dir = opendir( path );
if( dir == NULL)
return( POLARSSL_ERR_X509_FILE_IO_ERROR );
while( ( t_ret = readdir_r( dir, &entry, &result ) ) == 0 )
2012-06-04 14:46:42 +02:00
{
if( result == NULL )
break;
snprintf( entry_name, sizeof(entry_name), "%s/%s", path, entry.d_name );
i = stat( entry_name, &sb );
if( i == -1 )
{
closedir( dir );
return( POLARSSL_ERR_X509_FILE_IO_ERROR );
}
if( !S_ISREG( sb.st_mode ) )
2012-06-04 14:46:42 +02:00
continue;
// Ignore parse errors
//
2012-06-04 14:46:42 +02:00
t_ret = x509parse_crtfile( chain, entry_name );
if( t_ret < 0 )
ret++;
else
ret += t_ret;
2012-06-04 14:46:42 +02:00
}
closedir( dir );
#endif
return( ret );
}
/*
* Load a CSR into the structure
*/
int x509parse_csrfile( x509_csr *csr, const char *path )
{
int ret;
size_t n;
unsigned char *buf;
if ( ( ret = load_file( path, &buf, &n ) ) != 0 )
return( ret );
ret = x509parse_csr( csr, buf, n );
memset( buf, 0, n + 1 );
polarssl_free( buf );
return( ret );
}
/*
* Load one or more CRLs and add them to the chained list
*/
int x509parse_crlfile( x509_crl *chain, const char *path )
{
int ret;
size_t n;
unsigned char *buf;
2013-06-27 13:00:00 +02:00
if ( ( ret = load_file( path, &buf, &n ) ) != 0 )
return( ret );
ret = x509parse_crl( chain, buf, n );
memset( buf, 0, n + 1 );
polarssl_free( buf );
return( ret );
}
#if defined(POLARSSL_RSA_C)
/*
* Load and parse a private RSA key
*/
int x509parse_keyfile_rsa( rsa_context *rsa, const char *path, const char *pwd )
{
2013-08-14 13:39:57 +02:00
int ret;
pk_context pk;
pk_init( &pk );
2013-08-14 13:39:57 +02:00
ret = pk_parse_keyfile( &pk, path, pwd );
2013-08-14 13:39:57 +02:00
if( ret == 0 && ! pk_can_do( &pk, POLARSSL_PK_RSA ) )
ret = POLARSSL_ERR_PK_TYPE_MISMATCH;
2013-08-14 13:39:57 +02:00
if( ret == 0 )
rsa_copy( rsa, pk_rsa( pk ) );
2013-08-14 13:39:57 +02:00
else
rsa_free( rsa );
2013-08-14 13:39:57 +02:00
pk_free( &pk );
return( ret );
}
/*
* Load and parse a public RSA key
*/
int x509parse_public_keyfile_rsa( rsa_context *rsa, const char *path )
{
2013-08-14 13:39:57 +02:00
int ret;
pk_context pk;
pk_init( &pk );
2013-08-14 13:39:57 +02:00
ret = pk_parse_public_keyfile( &pk, path );
2013-08-14 13:39:57 +02:00
if( ret == 0 && ! pk_can_do( &pk, POLARSSL_PK_RSA ) )
ret = POLARSSL_ERR_PK_TYPE_MISMATCH;
2013-08-14 13:39:57 +02:00
if( ret == 0 )
rsa_copy( rsa, pk_rsa( pk ) );
2013-08-14 13:39:57 +02:00
else
rsa_free( rsa );
2013-08-14 13:39:57 +02:00
pk_free( &pk );
return( ret );
}
#endif /* POLARSSL_RSA_C */
#endif /* POLARSSL_FS_IO */
#if defined(POLARSSL_RSA_C)
/*
* Parse a private RSA key
*/
int x509parse_key_rsa( rsa_context *rsa,
const unsigned char *key, size_t keylen,
const unsigned char *pwd, size_t pwdlen )
{
2013-08-14 13:39:57 +02:00
int ret;
pk_context pk;
pk_init( &pk );
2013-08-14 13:39:57 +02:00
ret = pk_parse_key( &pk, key, keylen, pwd, pwdlen );
2013-08-14 13:39:57 +02:00
if( ret == 0 && ! pk_can_do( &pk, POLARSSL_PK_RSA ) )
ret = POLARSSL_ERR_PK_TYPE_MISMATCH;
2013-08-14 13:39:57 +02:00
if( ret == 0 )
rsa_copy( rsa, pk_rsa( pk ) );
2013-08-14 13:39:57 +02:00
else
rsa_free( rsa );
2013-08-14 13:39:57 +02:00
pk_free( &pk );
return( ret );
}
/*
* Parse a public RSA key
*/
int x509parse_public_key_rsa( rsa_context *rsa,
const unsigned char *key, size_t keylen )
{
2013-08-14 13:39:57 +02:00
int ret;
pk_context pk;
pk_init( &pk );
2013-08-14 13:39:57 +02:00
ret = pk_parse_public_key( &pk, key, keylen );
2013-08-14 13:39:57 +02:00
if( ret == 0 && ! pk_can_do( &pk, POLARSSL_PK_RSA ) )
ret = POLARSSL_ERR_PK_TYPE_MISMATCH;
2013-08-14 13:39:57 +02:00
if( ret == 0 )
rsa_copy( rsa, pk_rsa( pk ) );
2013-08-14 13:39:57 +02:00
else
rsa_free( rsa );
2013-08-14 13:39:57 +02:00
pk_free( &pk );
return( ret );
}
#endif /* POLARSSL_RSA_C */
#if defined _MSC_VER && !defined snprintf
#include <stdarg.h>
#if !defined vsnprintf
#define vsnprintf _vsnprintf
#endif // vsnprintf
/*
* Windows _snprintf and _vsnprintf are not compatible to linux versions.
* Result value is not size of buffer needed, but -1 if no fit is possible.
*
* This fuction tries to 'fix' this by at least suggesting enlarging the
* size by 20.
*/
static int compat_snprintf(char *str, size_t size, const char *format, ...)
{
va_list ap;
int res = -1;
va_start( ap, format );
res = vsnprintf( str, size, format, ap );
va_end( ap );
// No quick fix possible
if ( res < 0 )
return( (int) size + 20 );
return res;
}
#define snprintf compat_snprintf
#endif
#define POLARSSL_ERR_DEBUG_BUF_TOO_SMALL -2
#define SAFE_SNPRINTF() \
{ \
if( ret == -1 ) \
return( -1 ); \
\
if ( (unsigned int) ret > n ) { \
p[n - 1] = '\0'; \
return POLARSSL_ERR_DEBUG_BUF_TOO_SMALL;\
} \
\
n -= (unsigned int) ret; \
p += (unsigned int) ret; \
}
/*
* Store the name in printable form into buf; no more
* than size characters will be written
*/
int x509parse_dn_gets( char *buf, size_t size, const x509_name *dn )
{
int ret;
size_t i, n;
unsigned char c;
const x509_name *name;
const char *short_name = NULL;
char s[128], *p;
memset( s, 0, sizeof( s ) );
name = dn;
p = buf;
n = size;
while( name != NULL )
{
if( !name->oid.p )
{
name = name->next;
continue;
}
if( name != dn )
{
ret = snprintf( p, n, ", " );
SAFE_SNPRINTF();
}
ret = oid_get_attr_short_name( &name->oid, &short_name );
if( ret == 0 )
ret = snprintf( p, n, "%s=", short_name );
else
ret = snprintf( p, n, "\?\?=" );
SAFE_SNPRINTF();
for( i = 0; i < name->val.len; i++ )
{
if( i >= sizeof( s ) - 1 )
break;
c = name->val.p[i];
if( c < 32 || c == 127 || ( c > 128 && c < 160 ) )
s[i] = '?';
else s[i] = c;
}
s[i] = '\0';
ret = snprintf( p, n, "%s", s );
SAFE_SNPRINTF();
name = name->next;
}
return( (int) ( size - n ) );
}
/*
* Store the serial in printable form into buf; no more
* than size characters will be written
*/
int x509parse_serial_gets( char *buf, size_t size, const x509_buf *serial )
{
int ret;
size_t i, n, nr;
char *p;
p = buf;
n = size;
nr = ( serial->len <= 32 )
? serial->len : 28;
for( i = 0; i < nr; i++ )
{
if( i == 0 && nr > 1 && serial->p[i] == 0x0 )
continue;
ret = snprintf( p, n, "%02X%s",
serial->p[i], ( i < nr - 1 ) ? ":" : "" );
SAFE_SNPRINTF();
}
if( nr != serial->len )
{
ret = snprintf( p, n, "...." );
SAFE_SNPRINTF();
}
return( (int) ( size - n ) );
}
2013-08-12 19:45:32 +02:00
/*
* Helper for writing "RSA key size", "EC key size", etc
*/
static int x509_key_size_helper( char *buf, size_t size, const char *name )
{
char *p = buf;
size_t n = size;
int ret;
if( strlen( name ) + sizeof( " key size" ) > size )
return POLARSSL_ERR_DEBUG_BUF_TOO_SMALL;
ret = snprintf( p, n, "%s key size", name );
SAFE_SNPRINTF();
return( 0 );
}
/*
* Return an informational string about the certificate.
*/
2013-08-12 19:45:32 +02:00
#define BEFORE_COLON 14
#define BC "14"
int x509parse_cert_info( char *buf, size_t size, const char *prefix,
const x509_cert *crt )
{
int ret;
size_t n;
char *p;
const char *desc = NULL;
2013-08-12 19:45:32 +02:00
char key_size_str[BEFORE_COLON];
p = buf;
n = size;
ret = snprintf( p, n, "%scert. version : %d\n",
prefix, crt->version );
SAFE_SNPRINTF();
ret = snprintf( p, n, "%sserial number : ",
prefix );
SAFE_SNPRINTF();
ret = x509parse_serial_gets( p, n, &crt->serial);
SAFE_SNPRINTF();
ret = snprintf( p, n, "\n%sissuer name : ", prefix );
SAFE_SNPRINTF();
ret = x509parse_dn_gets( p, n, &crt->issuer );
SAFE_SNPRINTF();
ret = snprintf( p, n, "\n%ssubject name : ", prefix );
SAFE_SNPRINTF();
ret = x509parse_dn_gets( p, n, &crt->subject );
SAFE_SNPRINTF();
ret = snprintf( p, n, "\n%sissued on : " \
"%04d-%02d-%02d %02d:%02d:%02d", prefix,
crt->valid_from.year, crt->valid_from.mon,
crt->valid_from.day, crt->valid_from.hour,
crt->valid_from.min, crt->valid_from.sec );
SAFE_SNPRINTF();
ret = snprintf( p, n, "\n%sexpires on : " \
"%04d-%02d-%02d %02d:%02d:%02d", prefix,
crt->valid_to.year, crt->valid_to.mon,
crt->valid_to.day, crt->valid_to.hour,
crt->valid_to.min, crt->valid_to.sec );
SAFE_SNPRINTF();
ret = snprintf( p, n, "\n%ssigned using : ", prefix );
SAFE_SNPRINTF();
ret = oid_get_sig_alg_desc( &crt->sig_oid1, &desc );
if( ret != 0 )
ret = snprintf( p, n, "???" );
else
ret = snprintf( p, n, "%s", desc );
SAFE_SNPRINTF();
2013-08-12 19:45:32 +02:00
if( ( ret = x509_key_size_helper( key_size_str, BEFORE_COLON,
pk_get_name( &crt->pk ) ) ) != 0 )
2013-08-12 19:45:32 +02:00
{
return( ret );
}
ret = snprintf( p, n, "\n%s%-" BC "s: %d bits\n", prefix, key_size_str,
(int) pk_get_size( &crt->pk ) );
SAFE_SNPRINTF();
return( (int) ( size - n ) );
}
/*
* Return an informational string describing the given OID
*/
const char *x509_oid_get_description( x509_buf *oid )
{
const char *desc = NULL;
int ret;
ret = oid_get_extended_key_usage( oid, &desc );
if( ret != 0 )
return( NULL );
return( desc );
}
/* Return the x.y.z.... style numeric string for the given OID */
int x509_oid_get_numeric_string( char *buf, size_t size, x509_buf *oid )
{
return oid_get_numeric_string( buf, size, oid );
}
/*
* Return an informational string about the CRL.
*/
int x509parse_crl_info( char *buf, size_t size, const char *prefix,
const x509_crl *crl )
{
int ret;
size_t n;
char *p;
const char *desc;
const x509_crl_entry *entry;
p = buf;
n = size;
ret = snprintf( p, n, "%sCRL version : %d",
prefix, crl->version );
SAFE_SNPRINTF();
ret = snprintf( p, n, "\n%sissuer name : ", prefix );
SAFE_SNPRINTF();
ret = x509parse_dn_gets( p, n, &crl->issuer );
SAFE_SNPRINTF();
ret = snprintf( p, n, "\n%sthis update : " \
"%04d-%02d-%02d %02d:%02d:%02d", prefix,
crl->this_update.year, crl->this_update.mon,
crl->this_update.day, crl->this_update.hour,
crl->this_update.min, crl->this_update.sec );
SAFE_SNPRINTF();
ret = snprintf( p, n, "\n%snext update : " \
"%04d-%02d-%02d %02d:%02d:%02d", prefix,
crl->next_update.year, crl->next_update.mon,
crl->next_update.day, crl->next_update.hour,
crl->next_update.min, crl->next_update.sec );
SAFE_SNPRINTF();
entry = &crl->entry;
ret = snprintf( p, n, "\n%sRevoked certificates:",
prefix );
SAFE_SNPRINTF();
while( entry != NULL && entry->raw.len != 0 )
{
ret = snprintf( p, n, "\n%sserial number: ",
prefix );
SAFE_SNPRINTF();
ret = x509parse_serial_gets( p, n, &entry->serial);
SAFE_SNPRINTF();
ret = snprintf( p, n, " revocation date: " \
"%04d-%02d-%02d %02d:%02d:%02d",
entry->revocation_date.year, entry->revocation_date.mon,
entry->revocation_date.day, entry->revocation_date.hour,
entry->revocation_date.min, entry->revocation_date.sec );
SAFE_SNPRINTF();
entry = entry->next;
}
ret = snprintf( p, n, "\n%ssigned using : ", prefix );
SAFE_SNPRINTF();
ret = oid_get_sig_alg_desc( &crl->sig_oid1, &desc );
if( ret != 0 )
ret = snprintf( p, n, "???" );
else
ret = snprintf( p, n, "%s", desc );
SAFE_SNPRINTF();
2009-07-19 22:25:25 +02:00
ret = snprintf( p, n, "\n" );
SAFE_SNPRINTF();
return( (int) ( size - n ) );
}
/*
* Return an informational string about the CSR.
*/
int x509parse_csr_info( char *buf, size_t size, const char *prefix,
const x509_csr *csr )
{
int ret;
size_t n;
char *p;
const char *desc;
char key_size_str[BEFORE_COLON];
p = buf;
n = size;
ret = snprintf( p, n, "%sCSR version : %d",
prefix, csr->version );
SAFE_SNPRINTF();
ret = snprintf( p, n, "\n%ssubject name : ", prefix );
SAFE_SNPRINTF();
ret = x509parse_dn_gets( p, n, &csr->subject );
SAFE_SNPRINTF();
ret = snprintf( p, n, "\n%ssigned using : ", prefix );
SAFE_SNPRINTF();
ret = oid_get_sig_alg_desc( &csr->sig_oid, &desc );
if( ret != 0 )
ret = snprintf( p, n, "???" );
else
ret = snprintf( p, n, "%s", desc );
SAFE_SNPRINTF();
if( ( ret = x509_key_size_helper( key_size_str, BEFORE_COLON,
pk_get_name( &csr->pk ) ) ) != 0 )
{
return( ret );
}
ret = snprintf( p, n, "\n%s%-" BC "s: %d bits\n", prefix, key_size_str,
(int) pk_get_size( &csr->pk ) );
SAFE_SNPRINTF();
return( (int) ( size - n ) );
}
/*
* Return 0 if the x509_time is still valid, or 1 otherwise.
*/
#if defined(POLARSSL_HAVE_TIME)
int x509parse_time_expired( const x509_time *to )
{
int year, mon, day;
int hour, min, sec;
#if defined(_WIN32)
SYSTEMTIME st;
GetLocalTime(&st);
year = st.wYear;
mon = st.wMonth;
day = st.wDay;
hour = st.wHour;
min = st.wMinute;
sec = st.wSecond;
#else
struct tm *lt;
time_t tt;
tt = time( NULL );
lt = localtime( &tt );
year = lt->tm_year + 1900;
mon = lt->tm_mon + 1;
day = lt->tm_mday;
hour = lt->tm_hour;
min = lt->tm_min;
sec = lt->tm_sec;
#endif
if( year > to->year )
return( 1 );
if( year == to->year &&
mon > to->mon )
return( 1 );
if( year == to->year &&
mon == to->mon &&
day > to->day )
return( 1 );
if( year == to->year &&
mon == to->mon &&
day == to->day &&
hour > to->hour )
return( 1 );
if( year == to->year &&
mon == to->mon &&
day == to->day &&
hour == to->hour &&
min > to->min )
return( 1 );
if( year == to->year &&
mon == to->mon &&
day == to->day &&
hour == to->hour &&
min == to->min &&
sec > to->sec )
return( 1 );
return( 0 );
}
#else /* POLARSSL_HAVE_TIME */
int x509parse_time_expired( const x509_time *to )
{
((void) to);
return( 0 );
}
#endif /* POLARSSL_HAVE_TIME */
/*
* Return 1 if the certificate is revoked, or 0 otherwise.
*/
int x509parse_revoked( const x509_cert *crt, const x509_crl *crl )
{
const x509_crl_entry *cur = &crl->entry;
while( cur != NULL && cur->serial.len != 0 )
{
2011-01-16 22:38:35 +01:00
if( crt->serial.len == cur->serial.len &&
memcmp( crt->serial.p, cur->serial.p, crt->serial.len ) == 0 )
{
if( x509parse_time_expired( &cur->revocation_date ) )
return( 1 );
}
cur = cur->next;
}
return( 0 );
}
/*
* Check that the given certificate is valid accoring to the CRL.
*/
static int x509parse_verifycrl(x509_cert *crt, x509_cert *ca,
x509_crl *crl_list)
{
int flags = 0;
unsigned char hash[POLARSSL_MD_MAX_SIZE];
const md_info_t *md_info;
if( ca == NULL )
return( flags );
/*
* TODO: What happens if no CRL is present?
* Suggestion: Revocation state should be unknown if no CRL is present.
* For backwards compatibility this is not yet implemented.
*/
while( crl_list != NULL )
{
if( crl_list->version == 0 ||
crl_list->issuer_raw.len != ca->subject_raw.len ||
memcmp( crl_list->issuer_raw.p, ca->subject_raw.p,
crl_list->issuer_raw.len ) != 0 )
{
crl_list = crl_list->next;
continue;
}
/*
* Check if CRL is correctly signed by the trusted CA
*/
md_info = md_info_from_type( crl_list->sig_md );
if( md_info == NULL )
{
/*
* Cannot check 'unknown' hash
*/
flags |= BADCRL_NOT_TRUSTED;
break;
}
md( md_info, crl_list->tbs.p, crl_list->tbs.len, hash );
if( pk_can_do( &ca->pk, crl_list->sig_pk ) == 0 ||
pk_verify( &ca->pk, crl_list->sig_md, hash, md_info->size,
crl_list->sig.p, crl_list->sig.len ) != 0 )
{
flags |= BADCRL_NOT_TRUSTED;
break;
}
/*
* Check for validity of CRL (Do not drop out)
*/
if( x509parse_time_expired( &crl_list->next_update ) )
flags |= BADCRL_EXPIRED;
/*
* Check if certificate is revoked
*/
if( x509parse_revoked(crt, crl_list) )
{
flags |= BADCERT_REVOKED;
break;
}
crl_list = crl_list->next;
}
return flags;
}
// Equal == 0, inequal == 1
static int x509_name_cmp( const void *s1, const void *s2, size_t len )
{
size_t i;
unsigned char diff;
const unsigned char *n1 = s1, *n2 = s2;
for( i = 0; i < len; i++ )
{
diff = n1[i] ^ n2[i];
if( ( n1[i] >= 'a' || n1[i] <= 'z' ) && ( diff == 0 || diff == 32 ) )
continue;
if( ( n1[i] >= 'A' || n1[i] <= 'Z' ) && ( diff == 0 || diff == 32 ) )
continue;
return( 1 );
}
return( 0 );
}
static int x509_wildcard_verify( const char *cn, x509_buf *name )
{
size_t i;
size_t cn_idx = 0;
if( name->len < 3 || name->p[0] != '*' || name->p[1] != '.' )
return( 0 );
for( i = 0; i < strlen( cn ); ++i )
{
if( cn[i] == '.' )
{
cn_idx = i;
break;
}
}
if( cn_idx == 0 )
return( 0 );
if( strlen( cn ) - cn_idx == name->len - 1 &&
x509_name_cmp( name->p + 1, cn + cn_idx, name->len - 1 ) == 0 )
{
return( 1 );
}
return( 0 );
}
static int x509parse_verify_top(
x509_cert *child, x509_cert *trust_ca,
x509_crl *ca_crl, int path_cnt, int *flags,
int (*f_vrfy)(void *, x509_cert *, int, int *),
void *p_vrfy )
{
int ret;
int ca_flags = 0, check_path_cnt = path_cnt + 1;
unsigned char hash[POLARSSL_MD_MAX_SIZE];
const md_info_t *md_info;
if( x509parse_time_expired( &child->valid_to ) )
*flags |= BADCERT_EXPIRED;
/*
* Child is the top of the chain. Check against the trust_ca list.
*/
*flags |= BADCERT_NOT_TRUSTED;
2013-08-23 16:47:30 +02:00
md_info = md_info_from_type( child->sig_md );
if( md_info == NULL )
{
/*
* Cannot check 'unknown', no need to try any CA
*/
trust_ca = NULL;
}
else
md( md_info, child->tbs.p, child->tbs.len, hash );
while( trust_ca != NULL )
{
if( trust_ca->version == 0 ||
child->issuer_raw.len != trust_ca->subject_raw.len ||
memcmp( child->issuer_raw.p, trust_ca->subject_raw.p,
child->issuer_raw.len ) != 0 )
{
trust_ca = trust_ca->next;
continue;
}
/*
* Reduce path_len to check against if top of the chain is
* the same as the trusted CA
*/
if( child->subject_raw.len == trust_ca->subject_raw.len &&
memcmp( child->subject_raw.p, trust_ca->subject_raw.p,
child->issuer_raw.len ) == 0 )
{
check_path_cnt--;
}
if( trust_ca->max_pathlen > 0 &&
trust_ca->max_pathlen < check_path_cnt )
{
trust_ca = trust_ca->next;
continue;
}
if( pk_can_do( &trust_ca->pk, child->sig_pk ) == 0 ||
pk_verify( &trust_ca->pk, child->sig_md, hash, md_info->size,
child->sig.p, child->sig.len ) != 0 )
{
trust_ca = trust_ca->next;
continue;
2013-08-09 12:30:45 +02:00
}
/*
* Top of chain is signed by a trusted CA
*/
*flags &= ~BADCERT_NOT_TRUSTED;
break;
}
/*
* If top of chain is not the same as the trusted CA send a verify request
* to the callback for any issues with validity and CRL presence for the
* trusted CA certificate.
*/
if( trust_ca != NULL &&
( child->subject_raw.len != trust_ca->subject_raw.len ||
memcmp( child->subject_raw.p, trust_ca->subject_raw.p,
child->issuer_raw.len ) != 0 ) )
{
2013-08-23 16:47:30 +02:00
/* Check trusted CA's CRL for the chain's top crt */
*flags |= x509parse_verifycrl( child, trust_ca, ca_crl );
if( x509parse_time_expired( &trust_ca->valid_to ) )
ca_flags |= BADCERT_EXPIRED;
if( NULL != f_vrfy )
{
if( ( ret = f_vrfy( p_vrfy, trust_ca, path_cnt + 1, &ca_flags ) ) != 0 )
return( ret );
}
}
/* Call callback on top cert */
if( NULL != f_vrfy )
{
if( ( ret = f_vrfy(p_vrfy, child, path_cnt, flags ) ) != 0 )
return( ret );
}
*flags |= ca_flags;
return( 0 );
}
static int x509parse_verify_child(
x509_cert *child, x509_cert *parent, x509_cert *trust_ca,
x509_crl *ca_crl, int path_cnt, int *flags,
int (*f_vrfy)(void *, x509_cert *, int, int *),
void *p_vrfy )
{
int ret;
int parent_flags = 0;
unsigned char hash[POLARSSL_MD_MAX_SIZE];
x509_cert *grandparent;
const md_info_t *md_info;
if( x509parse_time_expired( &child->valid_to ) )
*flags |= BADCERT_EXPIRED;
md_info = md_info_from_type( child->sig_md );
if( md_info == NULL )
{
/*
* Cannot check 'unknown' hash
*/
*flags |= BADCERT_NOT_TRUSTED;
}
else
{
md( md_info, child->tbs.p, child->tbs.len, hash );
if( pk_can_do( &parent->pk, child->sig_pk ) == 0 ||
pk_verify( &parent->pk, child->sig_md, hash, md_info->size,
child->sig.p, child->sig.len ) != 0 )
{
*flags |= BADCERT_NOT_TRUSTED;
}
}
/* Check trusted CA's CRL for the given crt */
*flags |= x509parse_verifycrl(child, parent, ca_crl);
grandparent = parent->next;
while( grandparent != NULL )
{
if( grandparent->version == 0 ||
grandparent->ca_istrue == 0 ||
parent->issuer_raw.len != grandparent->subject_raw.len ||
memcmp( parent->issuer_raw.p, grandparent->subject_raw.p,
parent->issuer_raw.len ) != 0 )
{
grandparent = grandparent->next;
continue;
}
break;
}
if( grandparent != NULL )
{
/*
* Part of the chain
*/
ret = x509parse_verify_child( parent, grandparent, trust_ca, ca_crl, path_cnt + 1, &parent_flags, f_vrfy, p_vrfy );
if( ret != 0 )
return( ret );
}
else
{
ret = x509parse_verify_top( parent, trust_ca, ca_crl, path_cnt + 1, &parent_flags, f_vrfy, p_vrfy );
if( ret != 0 )
return( ret );
}
/* child is verified to be a child of the parent, call verify callback */
if( NULL != f_vrfy )
if( ( ret = f_vrfy( p_vrfy, child, path_cnt, flags ) ) != 0 )
return( ret );
*flags |= parent_flags;
return( 0 );
}
/*
* Verify the certificate validity
*/
int x509parse_verify( x509_cert *crt,
x509_cert *trust_ca,
x509_crl *ca_crl,
const char *cn, int *flags,
int (*f_vrfy)(void *, x509_cert *, int, int *),
void *p_vrfy )
{
size_t cn_len;
int ret;
int pathlen = 0;
x509_cert *parent;
x509_name *name;
x509_sequence *cur = NULL;
*flags = 0;
if( cn != NULL )
{
name = &crt->subject;
cn_len = strlen( cn );
if( crt->ext_types & EXT_SUBJECT_ALT_NAME )
{
cur = &crt->subject_alt_names;
while( cur != NULL )
{
if( cur->buf.len == cn_len &&
x509_name_cmp( cn, cur->buf.p, cn_len ) == 0 )
break;
if( cur->buf.len > 2 &&
memcmp( cur->buf.p, "*.", 2 ) == 0 &&
x509_wildcard_verify( cn, &cur->buf ) )
break;
cur = cur->next;
}
if( cur == NULL )
*flags |= BADCERT_CN_MISMATCH;
}
else
{
while( name != NULL )
{
if( OID_CMP( OID_AT_CN, &name->oid ) )
{
if( name->val.len == cn_len &&
x509_name_cmp( name->val.p, cn, cn_len ) == 0 )
break;
if( name->val.len > 2 &&
memcmp( name->val.p, "*.", 2 ) == 0 &&
x509_wildcard_verify( cn, &name->val ) )
break;
}
name = name->next;
}
if( name == NULL )
*flags |= BADCERT_CN_MISMATCH;
}
}
/*
* Iterate upwards in the given cert chain, to find our crt parent.
* Ignore any upper cert with CA != TRUE.
*/
parent = crt->next;
while( parent != NULL && parent->version != 0 )
{
if( parent->ca_istrue == 0 ||
crt->issuer_raw.len != parent->subject_raw.len ||
memcmp( crt->issuer_raw.p, parent->subject_raw.p,
crt->issuer_raw.len ) != 0 )
{
parent = parent->next;
continue;
}
break;
}
if( parent != NULL )
{
/*
* Part of the chain
*/
ret = x509parse_verify_child( crt, parent, trust_ca, ca_crl, pathlen, flags, f_vrfy, p_vrfy );
if( ret != 0 )
return( ret );
}
else
{
ret = x509parse_verify_top( crt, trust_ca, ca_crl, pathlen, flags, f_vrfy, p_vrfy );
if( ret != 0 )
return( ret );
}
if( *flags != 0 )
return( POLARSSL_ERR_X509_CERT_VERIFY_FAILED );
return( 0 );
}
/*
* Unallocate all certificate data
*/
void x509_free( x509_cert *crt )
{
x509_cert *cert_cur = crt;
x509_cert *cert_prv;
x509_name *name_cur;
x509_name *name_prv;
x509_sequence *seq_cur;
x509_sequence *seq_prv;
if( crt == NULL )
return;
do
{
pk_free( &cert_cur->pk );
name_cur = cert_cur->issuer.next;
while( name_cur != NULL )
{
name_prv = name_cur;
name_cur = name_cur->next;
memset( name_prv, 0, sizeof( x509_name ) );
polarssl_free( name_prv );
}
name_cur = cert_cur->subject.next;
while( name_cur != NULL )
{
name_prv = name_cur;
name_cur = name_cur->next;
memset( name_prv, 0, sizeof( x509_name ) );
polarssl_free( name_prv );
}
seq_cur = cert_cur->ext_key_usage.next;
while( seq_cur != NULL )
{
seq_prv = seq_cur;
seq_cur = seq_cur->next;
memset( seq_prv, 0, sizeof( x509_sequence ) );
polarssl_free( seq_prv );
}
2012-02-11 19:42:45 +01:00
seq_cur = cert_cur->subject_alt_names.next;
while( seq_cur != NULL )
{
seq_prv = seq_cur;
seq_cur = seq_cur->next;
memset( seq_prv, 0, sizeof( x509_sequence ) );
polarssl_free( seq_prv );
2012-02-11 19:42:45 +01:00
}
if( cert_cur->raw.p != NULL )
{
memset( cert_cur->raw.p, 0, cert_cur->raw.len );
polarssl_free( cert_cur->raw.p );
}
cert_cur = cert_cur->next;
}
while( cert_cur != NULL );
cert_cur = crt;
do
{
cert_prv = cert_cur;
cert_cur = cert_cur->next;
memset( cert_prv, 0, sizeof( x509_cert ) );
if( cert_prv != crt )
polarssl_free( cert_prv );
}
while( cert_cur != NULL );
}
/*
* Unallocate all CRL data
*/
void x509_crl_free( x509_crl *crl )
{
x509_crl *crl_cur = crl;
x509_crl *crl_prv;
x509_name *name_cur;
x509_name *name_prv;
x509_crl_entry *entry_cur;
x509_crl_entry *entry_prv;
if( crl == NULL )
return;
do
{
name_cur = crl_cur->issuer.next;
while( name_cur != NULL )
{
name_prv = name_cur;
name_cur = name_cur->next;
memset( name_prv, 0, sizeof( x509_name ) );
polarssl_free( name_prv );
}
entry_cur = crl_cur->entry.next;
while( entry_cur != NULL )
{
entry_prv = entry_cur;
entry_cur = entry_cur->next;
memset( entry_prv, 0, sizeof( x509_crl_entry ) );
polarssl_free( entry_prv );
}
if( crl_cur->raw.p != NULL )
{
memset( crl_cur->raw.p, 0, crl_cur->raw.len );
polarssl_free( crl_cur->raw.p );
}
crl_cur = crl_cur->next;
}
while( crl_cur != NULL );
crl_cur = crl;
do
{
crl_prv = crl_cur;
crl_cur = crl_cur->next;
memset( crl_prv, 0, sizeof( x509_crl ) );
if( crl_prv != crl )
polarssl_free( crl_prv );
}
while( crl_cur != NULL );
}
/*
* Unallocate all CSR data
*/
void x509_csr_free( x509_csr *csr )
{
x509_name *name_cur;
x509_name *name_prv;
if( csr == NULL )
return;
pk_free( &csr->pk );
name_cur = csr->subject.next;
while( name_cur != NULL )
{
name_prv = name_cur;
name_cur = name_cur->next;
memset( name_prv, 0, sizeof( x509_name ) );
polarssl_free( name_prv );
}
if( csr->raw.p != NULL )
{
memset( csr->raw.p, 0, csr->raw.len );
polarssl_free( csr->raw.p );
}
memset( csr, 0, sizeof( x509_csr ) );
}
#if defined(POLARSSL_SELF_TEST)
#include "polarssl/certs.h"
/*
* Checkup routine
*/
int x509_self_test( int verbose )
{
#if defined(POLARSSL_CERTS_C) && defined(POLARSSL_MD5_C)
int ret;
int flags;
x509_cert cacert;
x509_cert clicert;
pk_context pkey;
if( verbose != 0 )
printf( " X.509 certificate load: " );
memset( &clicert, 0, sizeof( x509_cert ) );
ret = x509parse_crt( &clicert, (const unsigned char *) test_cli_crt,
strlen( test_cli_crt ) );
if( ret != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
return( ret );
}
memset( &cacert, 0, sizeof( x509_cert ) );
ret = x509parse_crt( &cacert, (const unsigned char *) test_ca_crt,
strlen( test_ca_crt ) );
if( ret != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
return( ret );
}
#if defined(POLARSSL_MD5_C) && defined(POLARSSL_CIPHER_MODE_CBC) && \
defined(POLARSSL_DES_C) && defined(POLARSSL_AES_C)
if( verbose != 0 )
printf( "passed\n X.509 private key load: " );
pk_init( &pkey );
if( ( ret = pk_parse_key( &pkey,
(const unsigned char *) test_ca_key,
strlen( test_ca_key ),
(const unsigned char *) test_ca_pwd,
strlen( test_ca_pwd ) ) ) != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
return( ret );
}
#endif
if( verbose != 0 )
printf( "passed\n X.509 signature verify: ");
ret = x509parse_verify( &clicert, &cacert, NULL, NULL, &flags, NULL, NULL );
if( ret != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
printf("ret = %d, &flags = %04x\n", ret, flags);
return( ret );
}
if( verbose != 0 )
printf( "passed\n\n");
x509_free( &cacert );
x509_free( &clicert );
pk_free( &pkey );
return( 0 );
#else
((void) verbose);
return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE );
#endif
}
#endif
#endif