certgen.c 45 KB
Newer Older

static int attribute_eccpoint_text(cxml_handler_t* const _h, char * const text, int length)
{
	cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
	if (h->pk_data == NULL){
		h->pk_data = malloc(64);
		h->pk_datasize = 0;
	}
	char * e = cstr_hex2bin(h->pk_data + h->pk_datasize, 64 - h->pk_datasize, text, length);
	h->pk_datasize = e - h->pk_data;
	return 0;
}


static int attribute_assurance_tag  (cxml_handler_t* const _h, cxml_tag_t * const tag)
{
	int rc = 0;
	cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
	if (cxml_tag_is_open(tag)){
		uint8_t assurance = 0;
		const char * v = cxml_tag_attr_value(tag, "level");
		if(v){
			while(*v && cisspace(*v))v++;
			if(*v){
				long n = strtol(v, NULL, 0); 
				if(n<0 || n > 7){
					fprintf(stderr, "%s: Invalid assurance level\n", v);
					return -1;
				}
				assurance |= n<<5;
			}
		}
		v = cxml_tag_attr_value(tag, "confidence");
		if(v){
			while(*v && cisspace(*v))v++;
			if(*v){
				long n = strtol(v, NULL, 0); 
				if(n<0 || n > 3){
					fprintf(stderr, "%s: Invalid assurance confidence\n", v);
					return -1;
				}
				assurance |= n;
			}
		}
		cint8_write(assurance, &h->ptr, h->end, &rc);
	}
	return rc;
}

static int attribute_aid_tag(cxml_handler_t* const _h, cxml_tag_t * const tag)
{
	int rc = 0;
	cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
	if(0 == (tag->type & CXML_TAG_OPEN)){
		const char * v = cxml_tag_attr_value(tag, "value");
		if(NULL == v)v = cxml_tag_attr_value(tag, "aid");
		if(NULL == v){
			fprintf(stderr, "WARNING: Value required for AID tag. Item was skiped.\n");
		}else{
			uint32_t n;
			if (!cisdigit(*v)){
				// look in the aid map
				const char * v1 = macro_lookup(v);
				if (!v1){
					fprintf(stderr, "ERROR: Invalid AID '%s'\n", v);
					return -1;
				}
				v = v1;
			n = strtoul(v, NULL, 0);
			cintx_write(n, &h->ptr, h->end, &rc);
		}
	}
	return rc;
}

static int attribute_ssp_tag        (cxml_handler_t* const _h, cxml_tag_t * const tag)
{
	int rc = 0;
	cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
	if (cxml_tag_is_open(tag)){
		uint32_t n;
		const char * v = cxml_tag_attr_value(tag, "aid");
		if(NULL == v){
			fprintf(stderr, "ERROR: AID shall be supplied for SSP.\n");
			return -1;
		}
		if (!cisdigit(*v)){
			// look in the aid map
			const char * v1 = macro_lookup(v);
			if (!v1){
				fprintf(stderr, "ERROR: Invalid AID '%s' in SSP\n", v);
				return -1;
			}
			v = v1;
		}
		n = strtoul(v, NULL, 0);
		cintx_write(n, &h->ptr, h->end, &rc);
		bookmark_position(h, tag);
	}else{
		apply_bookmark_size(h, tag);
	}
	return rc;
}

static int attribute_ssp_text(cxml_handler_t* const _h, char * const text, int length)
{
	int rc=0;
	if (text && length){
		cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
		rc = cbuf_write(text, length, &h->ptr, h->end, NULL);
	}
	return rc;
}

static int certificate_validity_tag (cxml_handler_t* const _h, cxml_tag_t * const tag)
{
	int rc;
	cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
	if (cxml_tag_is_open(tag)){
		bookmark_position(h, tag);
		rc = 0;
	}else{
		rc = apply_bookmark_size(h, tag);
	}
	return rc;
}

filatov's avatar
filatov committed
static unsigned int _convert_diff_time(const char * v)
{
	unsigned int ret = 0;
	const char * p = v;
	char * e;
	int mult = 1;
	switch (*p){
	case '-':
		mult = -1;
	case '+':
		p++;
	}

	for (;;){
		unsigned int n;
		n = strtoul(p, &e, 10);
		if (n == ULONG_MAX) break;
		if (cisalnum(*e)){
			switch (*e){
			case 'd': n *= 24;
			case 'h': n *= 60;
			case 'm': n *= 60;
			case 's': e++; break;
			default:
				fprintf(stderr, "%s: Unknown time modificator: '%c'\n", p, *e);
				return INT32_MIN;
			}
		}
		else{
			// consider n as days
			n *= 24 * 3600;
		}
		ret += n;
		if (cisdigit(*e)){
			p = e;
			continue;
		}
		break;
	}
	return ret * mult;
}

static unsigned int _convert_time2(const char * v, unsigned int baseTime)
{
	unsigned int ret;
	char * e;
	struct tm tm;
	
	// can be a difference from the base time point
filatov's avatar
filatov committed
	if (*v == '-' || *v == '+'){
		if (baseTime == 0){
			fprintf(stderr, "Default time must be set\n");
			return 0;
		}
filatov's avatar
filatov committed
		ret = _convert_diff_time(v);
		if (ret == INT32_MIN)
			return 0;
		ret = baseTime + ret;
	}
	else{
		// next try load as integer seconds since epoch
		ret = strtoul(v, &e, 0);
		if (ret == ULONG_MAX || *e){
			ret = 0;
			memset(&tm, 0, sizeof(tm));
filatov's avatar
filatov committed
			// check predefined values
			if ((e = cstrisprefix(v, "today"))){
				time_t t;
				struct tm * ptm;
				time(&t); ptm = gmtime(&t);
				tm.tm_year = ptm->tm_year; tm.tm_mon = ptm->tm_mon; tm.tm_mday = ptm->tm_mday;
			}
			else if ((e = cstrisprefix(v, "ybegin"))){
				time_t t;
				struct tm * ptm;
				time(&t); ptm = gmtime(&t);
				tm.tm_year = ptm->tm_year; tm.tm_mon = 0; tm.tm_mday = 1;
			}
			else if ((e = cstrisprefix(v, "yend"))){
				time_t t;
				struct tm * ptm;
				time(&t); ptm = gmtime(&t);
				tm.tm_year = ptm->tm_year+1; tm.tm_mon = 0; tm.tm_mday = 1;
			}
			//next try to convert ISO text representation
			else if (3 == sscanf(v, "%d-%d-%d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday)){
				tm.tm_mon--; // STARTED FROM 0
filatov's avatar
filatov committed
				if (tm.tm_year > 500)
filatov's avatar
filatov committed
				e = (char*)v; // to prevent next check for '-/+'
			}
			else{
				fprintf(stderr, "%s: Date format specification error. Use YYY-MM-DD or today+/-NNNd\n", v);
				return 0;
			}
			ret = mkitstime32(&tm);
			if (ret == (time_t)-1) {
				fprintf(stderr, "%s: Date format specification error. Use YYY-MM-DD or today+/-NNNd\n", v);
				ret = 0;
			}
			if (*e == '-' || *e == '+'){
				ret = _convert_time2(e, ret);
filatov's avatar
filatov committed
static unsigned int _convert_time(const char * v)
{
	return _convert_time2(v, _defaultTime);
}

static int validity_restriction_tag (cxml_handler_t* const _h, cxml_tag_t * const tag)
{
	int rc = 0;
	cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
	if (cxml_tag_is_open(tag)){
		int vr_type = -1;
		int nTime;
		const char * v = cxml_tag_attr_value(tag, "type");
		const char *start, *end, *duration;
		if(NULL == v){
			fprintf(stderr, "ERROR: Restriction shall have a type.\n");
			return -1;
		}
		
		if(0 == strcmp("time", v)){
			start    = cxml_tag_attr_value(tag, "start");
			end      = cxml_tag_attr_value(tag, "end");
			duration = cxml_tag_attr_value(tag, "duration");
			if(end && *end){
				vr_type = (start && *start) ? 1 : 0;
			}else if(start && *start && duration && *duration){
				vr_type = 2;
			}else{
				fprintf(stderr, "ERROR: Either end or start and duration shall be specified for time restriction.\n");
				return -1;
			}
		}else if(0 == strcmp("region", v)){
			vr_type = 3;
			vr_type=strtoul(v, NULL,0);
		}else{
			fprintf(stderr, "%s: Unknown validity restriction type.\n", v);
			return -1;
		}
		h->vr_type = vr_type;
		cint8_write(vr_type, &h->ptr, h->end, &rc);
		
		// save time restrictions
		switch(vr_type){
		case 1: /* time_start_and_end */
			nTime = _convert_time(start);
			cint32_write(nTime, &h->ptr, h->end, &rc);
		case 0: /* time_end */
			nTime = _convert_time(end);
			cint32_write(nTime, &h->ptr, h->end, &rc);
			break;
		case 2: /* time_start_and_duration */
			nTime = _convert_time(start);
			cint32_write(nTime, &h->ptr, h->end, &rc);
filatov's avatar
filatov committed
			nTime = _convert_diff_time(duration);
			cint32_write(nTime, &h->ptr, h->end, &rc);
			break;
		case 3: /* region */
			break;
		default: // opaque
			bookmark_position(h, tag); // for opaque data
		}
	}else{
		if(h->vr_type > 3){
			apply_bookmark_size(h, tag);
		}
		h->vr_type = -1;
	}
	return 0;
}
static int region_none_tag (cxml_handler_t* const _h, cxml_tag_t * const tag)
{
	if (cxml_tag_is_open(tag)){
		cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
		return cint8_write(0, &h->ptr, h->end, NULL);
	}
	return 0;
}

static int region_circle_tag (cxml_handler_t* const _h, cxml_tag_t * const tag)
{
	int rc = 0;
	cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
	if (cxml_tag_is_open(tag)){
		//region type
		if(0 == cint8_write(1, &h->ptr, h->end, NULL)){
			//latitude and longitude
			rc = location_tag (_h, tag);
			if(0 == rc){
				//radius
				uint32_t n;
				const char * v    = cxml_tag_attr_value(tag, "radius");
				if(NULL == v){
					fprintf(stderr, "ERROR: radius shall be specified for circle.\n");
					return -1;
				}
				n = strtoul(v, &e, 0);
				if( (e[0] == 'k' || e[0] == 'K') && (e[1] == 'm' || e[1] == 'M') )
					n *= 1000;
				if(n > 0xFFFF){
					fprintf(stderr, "ERROR: %ul: radius is too big.\n", n);
					return -1;
				}
				cint16_write(n, &h->ptr, h->end, &rc);
			}
		}
	}
	return rc;
}

static int region_rectangle_tag (cxml_handler_t* const _h, cxml_tag_t * const tag)
{
	int rc;
	cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
	if (cxml_tag_is_open(tag)){
		// region type
		rc = cint8_write(2, &h->ptr, h->end, NULL);
		if(0 == rc){
			bookmark_position(h, tag);
		}
	}else{
		rc = apply_bookmark_size(h, tag);
	}
	return rc;
}

static int region_polygon_tag (cxml_handler_t* const _h, cxml_tag_t * const tag)
{
	int rc;
	cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
	if (cxml_tag_is_open(tag)){
		// region type
		rc = cint8_write(3, &h->ptr, h->end, NULL);
		if(0 == rc){
			bookmark_position(h, tag);
		}
	}else{
		rc = apply_bookmark_size(h, tag);
	}
	return rc;
}

static int location_tag (cxml_handler_t* const _h, cxml_tag_t * const tag)
{
	int rc = 0;
	if (cxml_tag_is_open(tag)){
filatov's avatar
filatov committed
		int32_t lat, lon, absolute = 0;
		cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
filatov's avatar
filatov committed

		const char * v = cxml_tag_attr_value(tag, "mode");
		absolute = cstrequal(v, "absolute");
			
		v = cxml_tag_attr_value(tag, "latitude");
		int relative = 1;
filatov's avatar
filatov committed
			fprintf(stderr, "ERROR: Latitude must be specified for location.\n");
		e = NULL; d = strtold(v, &e);
		if(e[0] == 'm' || (e[0] == 'k' && e[1] == 'm')){
			if(e[0] == 'k') d*=1000.0;
			d = d * _latTMDPerMetter;
		}else{
			if (d <= 90.0 &&  d >= -90.0) d *= 10000000.0; // degree
		}
filatov's avatar
filatov committed
		if (!absolute) d += _refLat;
		lat = (int32_t)floorl(d);

		v    = cxml_tag_attr_value(tag, "longitude");
		if(v == NULL){
			fprintf(stderr, "ERROR: Longitude shall be specified for location.\n");
			return -1;
		}
		e = NULL; d = strtold(v, &e);
		if(e[0] == 'm' || (e[0] == 'k' && e[1] == 'm')){
			if(e[0] == 'k') d*=1000.0;
			// convert metters to degree
			d = d * _lonTMDPerMetter;
		}else{
			if (d <= 180.0 &&  d >= -180.0) d *= 10000000.0; // degree
		}
filatov's avatar
filatov committed
		if (!absolute) d += _refLon;
		lon = (int32_t)floorl(d);

		cint32_write(lat, &h->ptr, h->end, &rc);
		cint32_write(lon, &h->ptr, h->end, &rc);
	}
	return rc;
}

static const char * _id_dictionaries[] = {
	"iso_3166_1",
	"un_stats",
};

static int region_id_tag (cxml_handler_t* const _h, cxml_tag_t * const tag)
{
	int rc = 0;
	cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
	if (cxml_tag_is_open(tag)){
		int value = 0;
		unsigned int uvalue = 0;
		const char * v;

		// region type
		rc = cint8_write(4, &h->ptr, h->end, NULL);

		// region dictionary. use 0 by default
		v = cxml_tag_attr_value(tag, "dictionary");
		if(v == NULL)v = cxml_tag_attr_value(tag, "dict");
		if(v){
			value = STR2ENUM(_id_dictionaries, v);
			if(value<0){
				fprintf(stderr, "%s: Unknown dictionary type\n", v);
				return -1;
			}
		}
		if(cint8_write(value, &h->ptr, h->end, NULL)){
			return -1;
		}
			
		v = cxml_tag_attr_value(tag, "id");
		if(v == NULL){
			fprintf(stderr, "ERROR: Region identifier must be set\n");
			return -1;
		}
filatov's avatar
filatov committed

		while (cisspace(*v))v++;
		if (!cisdigit(*v)){
			// look in the macro map
			const char * v1 = macro_lookup(v);
			if (!v1){
				fprintf(stderr, "%s: Invalid region identifier\n", v);
				return -1;
			}
			while (cisspace(*v1))v1++;
			v = v1;
		}
		uvalue = strtoul(v, NULL, 0);
		if(uvalue > 0xFFFF){
			fprintf(stderr, "%s: Invalid region identifier\n", v);
			return -1;
		}
		if(cint16_write(uvalue, &h->ptr, h->end, NULL)){
			return -1;
		}
			
		uvalue = 0;
		v = cxml_tag_attr_value(tag, "local");
		if(v){
			uvalue = strtoul(v, NULL, 0);
			if(!cisdigit(*v) || uvalue > 0xFFFF){
				fprintf(stderr, "%s: Invalid region identifier\n", v);
				return -1;
			}
		}
		cintx_write(uvalue, &h->ptr, h->end, &rc);
		if(rc) return -1;
	}
	return rc;
}

filatov's avatar
filatov committed
static const char * _region_types[] = {
	[0] = "none",
	[1] = "circle",
	[2] = "rectangle",
	[3] = "polygon",
	[4] = "id"
};
static cxml_tag_f* _region_type_handlers[] = {
	[0] = region_none_tag,
	[1] = region_circle_tag,
	[2] = region_rectangle_tag,
	[3] = region_polygon_tag,
	[4] = region_id_tag
};

static int region_region_tag(cxml_handler_t* const _h, cxml_tag_t * const tag)
{
	int rc = 0;
	cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
	if (cxml_tag_is_open(tag)){
		const char * v;
		v = cxml_tag_attr_value(tag, "type");
		h->nTmpValue = STR2ENUM(_region_types, v);
		if (h->nTmpValue < 0){
			fprintf(stderr, "ERROR: unknown region type: %s\n", v);
			return -1;
		}
	}
	if (h->nTmpValue < sizeof(_region_type_handlers) / sizeof(_region_type_handlers[0])){
		return _region_type_handlers[h->nTmpValue](_h, tag);
	}

	if (cxml_tag_is_open(tag)){
		// region type
		rc = cint8_write(h->nTmpValue, &h->ptr, h->end, NULL);
		bookmark_position(h, tag);
	} else{
		apply_bookmark_size(h, tag);
	}
	return rc;
}

static int region_region_text(cxml_handler_t* const _h, char * const text, int length)
{
	cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
	char * end;
	// try to treat it as hex
	end = cstr_hex2bin(h->ptr, h->end - h->ptr, text, length);
	if (end){
		h->ptr = end;
	}
	else{
		if (length > (h->end - h->ptr)){
			fprintf(stderr, "ERROR: region definition is tooooo big: %d bytes\n", length);
			return -1;
		}
		h->ptr = cmemcpy(h->ptr, text, length);
	}
	return 0;
}

static const char * _signature_algorithms[] = {
	"ecdsa_nistp256_with_sha256",
};

static int certificate_signature_tag (cxml_handler_t* const _h, cxml_tag_t * const tag)
{
	int rc = 0;
	cert_cxml_handler_t * h = (cert_cxml_handler_t *)_h;
	if (cxml_tag_is_open(tag)){
		void * key;
		int alg = 0;
		if(h->signer_type == 0){
			// self signed certificate
			key = h->verificationKey;
			if(!key){
				fprintf(stderr, "ERROR: Verification key attribute was not provided for self-signed certificate\n");
				return -1;
			}
		}else{
			const char * v = cxml_tag_attr_value(tag, "algorithm");
			if (v){
				alg = STR2ENUM(_signature_algorithms, v);
				if (alg < 0){
					fprintf(stderr, "%s: Unknown signature algorithm\n", v);
					return -1;
				}
			}

			v = cxml_tag_attr_value(tag, "signer");
			if (v){
				h->signer = v;
			}

			if (h->signer == NULL){
				fprintf(stderr, "ERROR: Signer certificate name shall be provided\n");
				return -1;
			}

			// load signer certificate
			int plen = strlen(_searchPath) + strlen(h->signer);
			char * path = malloc(plen + 16);

			cvstrncpy(path, plen + 16, _searchPath, "/", h->signer, ".vkey", NULL);
			key = ecc_api_key_private_load(path, alg);
			if (key == NULL){
				fprintf(stderr, "%s: Could not load issuing private key\n", path);
				free(path);
				return -1;
			}
		}
		cint8_write(alg, &h->ptr, h->end, &rc);
		rc = ecc_sign(key, h->buf, h->ptr - h->buf - 1, &h->ptr, h->end - h->ptr);
	}
	return rc;
}

static int  _Begin_Tag(cxml_handler_t* const h, cxml_tag_t * const tag)
{
	fprintf(stderr, "WARNING: %s: Unknown tag", tag->name);
	return 0;
}

static int  _End_Tag(cxml_handler_t* const h, cxml_tag_t * const tag)
{
	return 0;
}
static int  _Text(cxml_handler_t* const h, char * const text, int length)
{return 0;}