示例#1
0
/**
 * read the last serial number from file
 */
static chunk_t read_serial(void)
{
	chunk_t hex, serial = chunk_empty;
	char one[] = {0x01};
	FILE *fd;

	fd = fopen(OPENAC_SERIAL, "r");
	if (fd)
	{
		hex = chunk_alloca(64);
		hex.len = fread(hex.ptr, 1, hex.len, fd);
		if (hex.len)
		{
			/* remove any terminating newline character */
			if (hex.ptr[hex.len-1] == '\n')
			{
				hex.len--;
			}
			serial = chunk_alloca((hex.len / 2) + (hex.len % 2));
			serial = chunk_from_hex(hex, serial.ptr);
		}
		fclose(fd);
	}
	else
	{
		DBG1(DBG_LIB, "  file '%s' does not exist yet - serial number "
			 "set to 01", OPENAC_SERIAL);
	}
	if (!serial.len)
	{
		return chunk_clone(chunk_create(one, 1));
	}
	if (chunk_increment(serial))
	{	/* overflow, prepend 0x01 */
		return chunk_cat("cc", chunk_create(one, 1), serial);
	}
	return chunk_clone(serial);
}
示例#2
0
/**
 * Determine the type of the attribute and its value
 */
static bool parse_attributes(char *name, char *value, value_type_t *value_type,
							 configuration_attribute_type_t *type,
							 configuration_attribute_type_t *type_ip6,
							 chunk_t *blob)
{
	host_t *addr = NULL, *mask = NULL;
	chunk_t addr_chunk, mask_chunk, blob_next;
	char *text = "", *pos_addr, *pos_mask, *pos_next, *endptr;
	int i;

	switch (*value_type)
	{
		case VALUE_STRING:
			*blob = chunk_create(value, strlen(value));
			*blob = chunk_clone(*blob);
			break;
		case VALUE_HEX:
			*blob = chunk_from_hex(chunk_create(value, strlen(value)), NULL);
			break;
		case VALUE_ADDR:
			addr = host_create_from_string(value, 0);
			if (addr == NULL)
			{
				fprintf(stderr, "invalid IP address: '%s'.\n", value);
				return FALSE;
			}
			addr_chunk = addr->get_address(addr);
			*blob = chunk_clone(addr_chunk);
			break;
		case VALUE_SUBNET:
			*blob = chunk_empty;
			pos_next = value;

			do
			{
				pos_addr = pos_next;
				pos_next = strchr(pos_next, ',');
				if (pos_next)
				{
					*pos_next = '\0';
					pos_next += 1;
				}
				pos_mask = strchr(pos_addr, '/');
				if (pos_mask == NULL)
				{
					fprintf(stderr, "invalid IPv4 subnet: '%s'.\n", pos_addr);
					free(blob->ptr);
					return FALSE;
				}
				*pos_mask = '\0';
				pos_mask += 1;
				addr = host_create_from_string(pos_addr, 0);
				mask = host_create_from_string(pos_mask, 0);
				if (addr == NULL || addr->get_family(addr) != AF_INET ||
					mask == NULL || mask->get_family(addr) != AF_INET)
				{
					fprintf(stderr, "invalid IPv4 subnet: '%s/%s'.\n",
									pos_addr, pos_mask);
					DESTROY_IF(addr);
					DESTROY_IF(mask);
					free(blob->ptr);
					return FALSE;
				}
				addr_chunk = addr->get_address(addr);
				mask_chunk = mask->get_address(mask);
				blob_next = chunk_alloc(blob->len + UNITY_NETWORK_LEN);
				memcpy(blob_next.ptr, blob->ptr, blob->len);
				pos_addr = blob_next.ptr + blob->len;
				memset(pos_addr, 0x00, UNITY_NETWORK_LEN);
				memcpy(pos_addr,     addr_chunk.ptr, 4);
				memcpy(pos_addr + 4, mask_chunk.ptr, 4);
				addr->destroy(addr);
				addr = NULL;
				mask->destroy(mask);
				chunk_free(blob);
				*blob = blob_next;
			}
			while (pos_next);
			break;
		case VALUE_NONE:
			*blob = chunk_empty;
			break;
	}

	/* init the attribute type */
	*type     = 0;
	*type_ip6 = 0;

	for (i = 0; i < countof(attr_info); i++)
	{
		if (strcaseeq(name, attr_info[i].keyword))
		{
			*type      = attr_info[i].type;
			*type_ip6  = attr_info[i].type_ip6;

			if (*value_type == VALUE_NONE)
			{
				*value_type = attr_info[i].value_type;
				return TRUE;
			}

			if (*value_type != attr_info[i].value_type &&
				*value_type != VALUE_HEX)
			{
				switch (attr_info[i].value_type)
				{
					case VALUE_STRING:
						text = "a string";
						break;
					case VALUE_HEX:
						text = "a hex";
						break;
					case VALUE_ADDR:
						text = "an IP address";
						break;
					case VALUE_SUBNET:
						text = "a subnet";
						break;
					case VALUE_NONE:
						text = "no";
						break;
				}
				fprintf(stderr, "the %s attribute requires %s value.\n",
								 name, text);
				DESTROY_IF(addr);
				free(blob->ptr);
				return FALSE;
			}

			if (*value_type == VALUE_ADDR)
			{
				*type = (addr->get_family(addr) == AF_INET) ?
							attr_info[i].type : attr_info[i].type_ip6;
				addr->destroy(addr);
			}
			else if (*value_type == VALUE_HEX)
			{
				*value_type = attr_info[i].value_type;

				if (*value_type == VALUE_ADDR)
				{
					if (blob->len == 16)
					{
						*type = attr_info[i].type_ip6;
					}
					else if (blob->len != 4)
					{
						fprintf(stderr, "the %s attribute requires "
										"a valid IP address.\n", name);
						free(blob->ptr);
						return FALSE;
					}
				}
			}
			return TRUE;
		}
	}

	/* clean up */
	DESTROY_IF(addr);

	/* is the attribute type numeric? */
	*type = strtol(name, &endptr, 10);

	if (*endptr != '\0')
	{
		fprintf(stderr, "the %s attribute is not recognized.\n", name);
		free(blob->ptr);
		return FALSE;
	}
	if (*type < 1 || *type > 32767)
	{
		fprintf(stderr, "the attribute type must lie in the range 1..32767.\n");
		free(blob->ptr);
		return FALSE;
	}
	if (*value_type == VALUE_NONE)
	{
		*value_type = VALUE_HEX;
	}
	return TRUE;
}
示例#3
0
static bool get_next_test_vector(test_vector_t *test)
{
	param_t param = PARAM_UNKNOWN;
	char line[512];

	memset(test, 0, sizeof(test_vector_t));

	while (fgets(line, sizeof(line), ctx.in))
	{
		enumerator_t *enumerator;
		chunk_t value = chunk_empty;
		char *token;
		int i;

		switch (line[0])
		{
			case '\n':
			case '\r':
			case '#':
			case '\0':
				/* copy comments, empty lines etc. directly to the output */
				if (param != PARAM_UNKNOWN)
				{	/* seems we got a complete test vector */
					return TRUE;
				}
				fputs(line, ctx.out);
				continue;
			case '[':
				/* control directives */
				fputs(line, ctx.out);
				if (strpfx(line, "[ENCRYPT]"))
				{
					ctx.decrypt = FALSE;
				}
				else if (strpfx(line, "[DECRYPT]"))
				{
					ctx.decrypt = TRUE;
				}
				else if (strcasepfx(line, "[IVlen = "))
				{
					ctx.ivlen = atoi(line + strlen("[IVlen = "));
				}
				else if (strcasepfx(line, "[Taglen = "))
				{
					ctx.icvlen = atoi(line + strlen("[Taglen = "));
				}
				continue;
			default:
				/* we assume the rest of the lines are PARAM = VALUE pairs*/
				fputs(line, ctx.out);
				break;
		}

		i = 0;
		enumerator = enumerator_create_token(line, "=", " \n\r");
		while (enumerator->enumerate(enumerator, &token))
		{
			switch (i++)
			{
				case 0: /* PARAM */
					param = parse_parameter(token);
					continue;
				case 1: /* VALUE */
					if (param != PARAM_UNKNOWN && param != PARAM_COUNT)
					{
						value = chunk_from_hex(chunk_from_str(token), NULL);
					}
					else
					{
						value = chunk_empty;
					}
					continue;
				default:
					break;
			}
			break;
		}
		enumerator->destroy(enumerator);
		if (i < 2)
		{
			value = chunk_empty;
		}
		switch (param)
		{
			case PARAM_KEY:
				test->key = value;
				break;
			case PARAM_IV:
				test->iv = value;
				test->external_iv = TRUE;
				break;
			case PARAM_PLAINTEXT:
				test->plain = value;
				break;
			case PARAM_CIPHERTEXT:
				test->cipher = value;
				break;
			case PARAM_AAD:
				test->aad = value;
				break;
			case PARAM_ICV:
				test->icv = value;
				break;
			default:
				chunk_free(&value);
				break;
		}
	}
	if (param != PARAM_UNKNOWN)
	{	/* could be that the file ended with a complete test vector */
		return TRUE;
	}
	return FALSE;
}
示例#4
0
END_TEST

/*******************************************************************************
 * BASE16 encoding test
 */

START_TEST(test_base16)
{
	/* test vectors from RFC 4648:
	 *
	 * BASE16("") = ""
	 * BASE16("f") = "66"
	 * BASE16("fo") = "666F"
	 * BASE16("foo") = "666F6F"
	 * BASE16("foob") = "666F6F62"
	 * BASE16("fooba") = "666F6F6261"
	 * BASE16("foobar") = "666F6F626172"
	 */
	typedef struct {
		bool upper;
		char *in;
		char *out;
	} testdata_t;

	testdata_t test[] = {
		{TRUE,  "", ""},
		{TRUE,  "f", "66"},
		{TRUE,  "fo", "666F"},
		{TRUE,  "foo", "666F6F"},
		{TRUE,  "foob", "666F6F62"},
		{TRUE,  "fooba", "666F6F6261"},
		{TRUE,  "foobar", "666F6F626172"},
		{FALSE, "", ""},
		{FALSE, "f", "66"},
		{FALSE, "fo", "666f"},
		{FALSE, "foo", "666f6f"},
		{FALSE, "foob", "666f6f62"},
		{FALSE, "fooba", "666f6f6261"},
		{FALSE, "foobar", "666f6f626172"},
	};
	testdata_t test_colon[] = {
		{TRUE,  "", ""},
		{TRUE,  "f", "66"},
		{TRUE,  "fo", "66:6F"},
		{TRUE,  "foo", "66:6F:6F"},
		{FALSE, "foob", "66:6f:6f:62"},
		{FALSE, "fooba", "66:6f:6f:62:61"},
		{FALSE, "foobar", "66:6f:6f:62:61:72"},
		{FALSE, "foobar", "66:6f6f:6261:72"},
	};
	int i;

	for (i = 0; i < countof(test); i++)
	{
		chunk_t out;

		out = chunk_to_hex(chunk_create(test[i].in, strlen(test[i].in)), NULL,
						   test[i].upper);
		ck_assert_str_eq(out.ptr, test[i].out);
		free(out.ptr);
	}

	for (i = 0; i < countof(test); i++)
	{
		chunk_t out;

		out = chunk_from_hex(chunk_create(test[i].out, strlen(test[i].out)), NULL);
		fail_unless(strneq(out.ptr, test[i].in, out.len),
					"base16 conversion error - should '%s', is %#B",
					test[i].in, &out);
		free(out.ptr);
	}

	for (i = 0; i < countof(test_colon); i++)
	{
		chunk_t out;

		out = chunk_from_hex(chunk_create(test_colon[i].out, strlen(test_colon[i].out)), NULL);
		fail_unless(strneq(out.ptr, test_colon[i].in, out.len),
					"base16 conversion error - should '%s', is %#B",
					test_colon[i].in, &out);
		free(out.ptr);
	}
}
示例#5
0
/**
 * Sign a CRL
 */
static int sign_crl()
{
	cred_encoding_type_t form = CERT_ASN1_DER;
	private_key_t *private = NULL;
	public_key_t *public = NULL;
	certificate_t *ca = NULL, *crl = NULL;
	crl_t *lastcrl = NULL;
	x509_t *x509;
	hash_algorithm_t digest = HASH_UNKNOWN;
	signature_params_t *scheme = NULL;
	char *arg, *cacert = NULL, *cakey = NULL, *lastupdate = NULL, *error = NULL;
	char *basecrl = NULL;
	char serial[512], *keyid = NULL;
	int serial_len;
	crl_reason_t reason = CRL_REASON_UNSPECIFIED;
	time_t thisUpdate, nextUpdate, date = time(NULL);
	time_t lifetime = 15 * 24 * 60 * 60;
	char *datetu = NULL, *datenu = NULL, *dateform = NULL;
	linked_list_t *list, *cdps;
	enumerator_t *enumerator, *lastenum = NULL;
	x509_cdp_t *cdp;
	chunk_t crl_serial = chunk_empty, baseCrlNumber = chunk_empty;
	chunk_t encoding = chunk_empty;
	bool pss = lib->settings->get_bool(lib->settings, "%s.rsa_pss", FALSE,
									   lib->ns);

	list = linked_list_create();
	cdps = linked_list_create();

	while (TRUE)
	{
		switch (command_getopt(&arg))
		{
			case 'h':
				goto usage;
			case 'g':
				if (!enum_from_name(hash_algorithm_short_names, arg, &digest))
				{
					error = "invalid --digest type";
					goto usage;
				}
				continue;
			case 'R':
				if (streq(arg, "pss"))
				{
					pss = TRUE;
				}
				else if (!streq(arg, "pkcs1"))
				{
					error = "invalid RSA padding";
					goto usage;
				}
				continue;
			case 'c':
				cacert = arg;
				continue;
			case 'k':
				cakey = arg;
				continue;
			case 'x':
				keyid = arg;
				continue;
			case 'a':
				lastupdate = arg;
				continue;
			case 'l':
				lifetime = atoi(arg) * 24 * 60 * 60;
				if (!lifetime)
				{
					error = "invalid --lifetime value";
					goto usage;
				}
				continue;
			case 'D':
				dateform = arg;
				continue;
			case 'F':
				datetu = arg;
				continue;
			case 'T':
				datenu = arg;
				continue;
			case 'z':
				serial_len = read_serial(arg, serial, sizeof(serial));
				if (serial_len < 0)
				{
					snprintf(serial, sizeof(serial),
							 "parsing certificate '%s' failed", arg);
					error = serial;
					goto error;
				}
				add_revoked(list, chunk_create(serial, serial_len), reason, date);
				date = time(NULL);
				reason = CRL_REASON_UNSPECIFIED;
				continue;
			case 's':
			{
				chunk_t chunk;
				int hex_len;

				hex_len = strlen(arg);
				if ((hex_len / 2) + (hex_len % 2) > sizeof(serial))
				{
					error = "invalid serial";
					goto usage;
				}
				chunk = chunk_from_hex(chunk_create(arg, hex_len), serial);
				serial_len = chunk.len;
				add_revoked(list, chunk_create(serial, serial_len), reason, date);
				date = time(NULL);
				reason = CRL_REASON_UNSPECIFIED;
				continue;
			}
			case 'b':
				basecrl = arg;
				continue;
			case 'u':
				INIT(cdp,
					.uri = strdup(arg),
				);
				cdps->insert_last(cdps, cdp);
				continue;
			case 'r':
				if (streq(arg, "key-compromise"))
				{
					reason = CRL_REASON_KEY_COMPROMISE;
				}
				else if (streq(arg, "ca-compromise"))
				{
					reason = CRL_REASON_CA_COMPROMISE;
				}
				else if (streq(arg, "affiliation-changed"))
				{
					reason = CRL_REASON_AFFILIATION_CHANGED;
				}
				else if (streq(arg, "superseded"))
				{
					reason = CRL_REASON_SUPERSEDED;
				}
				else if (streq(arg, "cessation-of-operation"))
				{
					reason = CRL_REASON_CESSATION_OF_OPERATON;
				}
				else if (streq(arg, "certificate-hold"))
				{
					reason = CRL_REASON_CERTIFICATE_HOLD;
				}
				else
				{
					error = "invalid revocation reason";
					goto usage;
				}
				continue;
			case 'd':
				date = atol(arg);
				if (!date)
				{
					error = "invalid date";
					goto usage;
				}
				continue;
			case 'f':
				if (!get_form(arg, &form, CRED_CERTIFICATE))
				{
					error = "invalid output format";
					goto usage;
				}
				continue;
			case EOF:
				break;
			default:
				error = "invalid --signcrl option";
				goto usage;
		}
		break;
	}

	if (!cacert)
	{
		error = "--cacert is required";
		goto usage;
	}
	if (!cakey && !keyid)
	{
		error = "--cakey or --keyid is required";
		goto usage;
	}
	if (!calculate_lifetime(dateform, datetu, datenu, lifetime,
							&thisUpdate, &nextUpdate))
	{
		error = "invalid --this/next-update datetime";
		goto usage;
	}

	ca = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
							BUILD_FROM_FILE, cacert, BUILD_END);
	if (!ca)
	{
		error = "parsing CA certificate failed";
		goto error;
	}
	x509 = (x509_t*)ca;
	if (!(x509->get_flags(x509) & (X509_CA | X509_CRL_SIGN)))
	{
		error = "CA certificate misses CA basicConstraint / CRLSign keyUsage";
		goto error;
	}
	public = ca->get_public_key(ca);
示例#6
0
/**
 * Converts a PEM encoded file into its binary form (RFC 1421, RFC 934)
 */
static status_t pem_to_bin(chunk_t *blob, bool *pgp)
{
	typedef enum {
		PEM_PRE    = 0,
		PEM_MSG    = 1,
		PEM_HEADER = 2,
		PEM_BODY   = 3,
		PEM_POST   = 4,
		PEM_ABORT  = 5
	} state_t;

	encryption_algorithm_t alg = ENCR_UNDEFINED;
	size_t key_size = 0;
	bool encrypted = FALSE;
	state_t state  = PEM_PRE;
	chunk_t src    = *blob;
	chunk_t dst    = *blob;
	chunk_t line   = chunk_empty;
	chunk_t iv     = chunk_empty;
	u_char iv_buf[HASH_SIZE_MD5];
	status_t status = NOT_FOUND;
	enumerator_t *enumerator;
	shared_key_t *shared;

	dst.len = 0;
	iv.ptr = iv_buf;
	iv.len = 0;

	while (fetchline(&src, &line))
	{
		if (state == PEM_PRE)
		{
			if (find_boundary("BEGIN", &line))
			{
				state = PEM_MSG;
			}
			continue;
		}
		else
		{
			if (find_boundary("END", &line))
			{
				state = PEM_POST;
				break;
			}
			if (state == PEM_MSG)
			{
				state = PEM_HEADER;
				if (memchr(line.ptr, ':', line.len) == NULL)
				{
					state = PEM_BODY;
				}
			}
			if (state == PEM_HEADER)
			{
				err_t ugh = NULL;
				chunk_t name  = chunk_empty;
				chunk_t value = chunk_empty;

				/* an empty line separates HEADER and BODY */
				if (line.len == 0)
				{
					state = PEM_BODY;
					continue;
				}

				/* we are looking for a parameter: value pair */
				DBG2(DBG_ASN, "  %.*s", (int)line.len, line.ptr);
				ugh = extract_parameter_value(&name, &value, &line);
				if (ugh != NULL)
				{
					continue;
				}
				if (match("Proc-Type", &name) && *value.ptr == '4')
				{
					encrypted = TRUE;
				}
				else if (match("DEK-Info", &name))
				{
					chunk_t dek;

					if (!extract_token(&dek, ',', &value))
					{
						dek = value;
					}
					if (match("DES-EDE3-CBC", &dek))
					{
						alg = ENCR_3DES;
						key_size = 24;
					}
					else if (match("AES-128-CBC", &dek))
					{
						alg = ENCR_AES_CBC;
						key_size = 16;
					}
					else if (match("AES-192-CBC", &dek))
					{
						alg = ENCR_AES_CBC;
						key_size = 24;
					}
					else if (match("AES-256-CBC", &dek))
					{
						alg = ENCR_AES_CBC;
						key_size = 32;
					}
					else
					{
						DBG1(DBG_ASN, "  encryption algorithm '%.*s'"
							 " not supported", (int)dek.len, dek.ptr);
						return NOT_SUPPORTED;
					}
					if (!eat_whitespace(&value) || value.len > 2*sizeof(iv_buf))
					{
						return PARSE_ERROR;
					}
					iv = chunk_from_hex(value, iv_buf);
				}
			}
			else /* state is PEM_BODY */
			{
				chunk_t data;

				/* remove any trailing whitespace */
				if (!extract_token(&data ,' ', &line))
				{
					data = line;
				}

				/* check for PGP armor checksum */
				if (*data.ptr == '=')
				{
					*pgp = TRUE;
					data.ptr++;
					data.len--;
					DBG2(DBG_ASN, "  armor checksum: %.*s", (int)data.len,
						 data.ptr);
					continue;
				}

				if (blob->len - dst.len < data.len / 4 * 3)
				{
					state = PEM_ABORT;
				}
				data = chunk_from_base64(data, dst.ptr);

				dst.ptr += data.len;
				dst.len += data.len;
			}
		}
	}
	/* set length to size of binary blob */
	blob->len = dst.len;

	if (state != PEM_POST)
	{
		DBG1(DBG_LIB, "  file coded in unknown format, discarded");
		return PARSE_ERROR;
	}
	if (!encrypted)
	{
		return SUCCESS;
	}

	enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
											SHARED_PRIVATE_KEY_PASS, NULL, NULL);
	while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
	{
		chunk_t passphrase, chunk;

		passphrase = shared->get_key(shared);
		chunk = chunk_clone(*blob);
		status = pem_decrypt(&chunk, alg, key_size, iv, passphrase);
		if (status == SUCCESS)
		{
			memcpy(blob->ptr, chunk.ptr, chunk.len);
			blob->len = chunk.len;
		}
		free(chunk.ptr);
		if (status != INVALID_ARG)
		{	/* try again only if passphrase invalid */
			break;
		}
	}
	enumerator->destroy(enumerator);
	return status;
}
示例#7
0
/**
 * Converts a PEM encoded file into its binary form (RFC 1421, RFC 934)
 */
static status_t pem_to_bin(chunk_t *blob, chunk_t(*cb)(void*,int), void *cb_data,
						   bool *pgp)
{
	typedef enum {
		PEM_PRE    = 0,
		PEM_MSG    = 1,
		PEM_HEADER = 2,
		PEM_BODY   = 3,
		PEM_POST   = 4,
		PEM_ABORT  = 5
	} state_t;

	encryption_algorithm_t alg = ENCR_UNDEFINED;
	size_t key_size = 0;
	bool encrypted = FALSE;
	state_t state  = PEM_PRE;
	chunk_t src    = *blob;
	chunk_t dst    = *blob;
	chunk_t line   = chunk_empty;
	chunk_t iv     = chunk_empty;
	chunk_t passphrase;
	int try = 0;
	u_char iv_buf[HASH_SIZE_MD5];

	dst.len = 0;
	iv.ptr = iv_buf;
	iv.len = 0;

	while (fetchline(&src, &line))
	{
		if (state == PEM_PRE)
		{
			if (find_boundary("BEGIN", &line))
			{
				state = PEM_MSG;
			}
			continue;
		}
		else
		{
			if (find_boundary("END", &line))
			{
				state = PEM_POST;
				break;
			}
			if (state == PEM_MSG)
			{
				state = PEM_HEADER;
				if (memchr(line.ptr, ':', line.len) == NULL)
				{
					state = PEM_BODY;
				}
			}
			if (state == PEM_HEADER)
			{
				err_t ugh = NULL;
				chunk_t name  = chunk_empty;
				chunk_t value = chunk_empty;

				/* an empty line separates HEADER and BODY */
				if (line.len == 0)
				{
					state = PEM_BODY;
					continue;
				}

				/* we are looking for a parameter: value pair */
				DBG2(DBG_LIB, "  %.*s", (int)line.len, line.ptr);
				ugh = extract_parameter_value(&name, &value, &line);
				if (ugh != NULL)
				{
					continue;
				}
				if (match("Proc-Type", &name) && *value.ptr == '4')
				{
					encrypted = TRUE;
				}
				else if (match("DEK-Info", &name))
				{
					chunk_t dek;

					if (!extract_token(&dek, ',', &value))
					{
						dek = value;
					}
					if (match("DES-EDE3-CBC", &dek))
					{
						alg = ENCR_3DES;
						key_size = 24;
					}
					else if (match("AES-128-CBC", &dek))
					{
						alg = ENCR_AES_CBC;
						key_size = 16;
					}
					else if (match("AES-192-CBC", &dek))
					{
						alg = ENCR_AES_CBC;
						key_size = 24;
					}
					else if (match("AES-256-CBC", &dek))
					{
						alg = ENCR_AES_CBC;
						key_size = 32;
					}
					else
					{
						DBG1(DBG_LIB, "  encryption algorithm '%.*s'"
							 " not supported", dek.len, dek.ptr);
						return NOT_SUPPORTED;
					}
					eat_whitespace(&value);
					iv = chunk_from_hex(value, iv.ptr);
				}
			}
			else /* state is PEM_BODY */
			{
				chunk_t data;

				/* remove any trailing whitespace */
				if (!extract_token(&data ,' ', &line))
				{
					data = line;
				}

				/* check for PGP armor checksum */
				if (*data.ptr == '=')
				{
					*pgp = TRUE;
					data.ptr++;
					data.len--;
					DBG2(DBG_LIB, "  armor checksum: %.*s", (int)data.len,
						 data.ptr);
					continue;
				}

				if (blob->len - dst.len < data.len / 4 * 3)
				{
					state = PEM_ABORT;
				}
				data = chunk_from_base64(data, dst.ptr);

				dst.ptr += data.len;
				dst.len += data.len;
			}
		}
	}
	/* set length to size of binary blob */
	blob->len = dst.len;

	if (state != PEM_POST)
	{
		DBG1(DBG_LIB, "  file coded in unknown format, discarded");
		return PARSE_ERROR;
	}
	if (!encrypted)
	{
		return SUCCESS;
	}
	if (!cb)
	{
		DBG1(DBG_LIB, "  missing passphrase");
		return INVALID_ARG;
	}
	while (TRUE)
	{
		passphrase = cb(cb_data, ++try);
		if (!passphrase.len || !passphrase.ptr)
		{
			return INVALID_ARG;
		}
		switch (pem_decrypt(blob, alg, key_size, iv, passphrase))
		{
			case INVALID_ARG:
				/* bad passphrase, retry */
				continue;
			case SUCCESS:
				return SUCCESS;
			default:
				return FAILED;
		}
	}
}