int crypt_activate_by_volume_key(struct crypt_device *cd,
	const char *name,
	const char *volume_key,
	size_t volume_key_size,
	uint32_t flags)
{
	bool use_keyring;
	struct volume_key *vk = NULL;
	int r;

	if (!cd ||
	    ((flags & CRYPT_ACTIVATE_KEYRING_KEY) && !crypt_use_keyring_for_vk(cd)))
		return -EINVAL;

	log_dbg(cd, "%s volume %s by volume key.", name ? "Activating" : "Checking",
		name ?: "");

	r = _activate_check_status(cd, name, flags & CRYPT_ACTIVATE_REFRESH);
	if (r < 0)
		return r;

	r = _check_header_data_overlap(cd, name);
	if (r < 0)
		return r;

	/* use key directly, no hash */
	if (isPLAIN(cd->type)) {
		if (!name)
			return -EINVAL;

		if (!volume_key || !volume_key_size || volume_key_size != cd->u.plain.key_size) {
			log_err(cd, _("Incorrect volume key specified for plain device."));
			return -EINVAL;
		}

		vk = crypt_alloc_volume_key(volume_key_size, volume_key);
		if (!vk)
			return -ENOMEM;

		r = PLAIN_activate(cd, name, vk, cd->u.plain.hdr.size, flags);
	} else if (isLUKS1(cd->type)) {
		/* If key is not provided, try to use internal key */
		if (!volume_key) {
			if (!cd->volume_key) {
				log_err(cd, _("Volume key does not match the volume."));
				return -EINVAL;
			}
			volume_key_size = cd->volume_key->keylength;
			volume_key = cd->volume_key->key;
		}

		vk = crypt_alloc_volume_key(volume_key_size, volume_key);
		if (!vk)
			return -ENOMEM;
		r = LUKS_verify_volume_key(&cd->u.luks1.hdr, vk);

		if (r == -EPERM)
			log_err(cd, _("Volume key does not match the volume."));

		if (!r && name)
			r = LUKS1_activate(cd, name, vk, flags);
	} else if (isLUKS2(cd->type)) {
		/* If key is not provided, try to use internal key */
		if (!volume_key) {
			if (!cd->volume_key) {
				log_err(cd, _("Volume key does not match the volume."));
				return -EINVAL;
			}
			volume_key_size = cd->volume_key->keylength;
			volume_key = cd->volume_key->key;
		}

		vk = crypt_alloc_volume_key(volume_key_size, volume_key);
		if (!vk)
			return -ENOMEM;

		r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk);
		if (r == -EPERM || r == -ENOENT)
			log_err(cd, _("Volume key does not match the volume."));
		if (r > 0)
			r = 0;

		if (!crypt_use_keyring_for_vk(cd))
			use_keyring = false;
		else
			use_keyring = (name && !crypt_is_cipher_null(crypt_get_cipher(cd))) || (flags & CRYPT_ACTIVATE_KEYRING_KEY);

		if (!r && use_keyring) {
			r = LUKS2_key_description_by_segment(cd,
				&cd->u.luks2.hdr, vk, CRYPT_DEFAULT_SEGMENT);
			if (!r)
				r = crypt_volume_key_load_in_keyring(cd, vk);
			if (!r)
				flags |= CRYPT_ACTIVATE_KEYRING_KEY;
		}

		if (!r && name)
			r = LUKS2_activate(cd, name, vk, flags);
	} else if (isVERITY(cd->type)) {
		r = crypt_activate_by_signed_key(cd, name, volume_key, volume_key_size, NULL, 0, flags);
	} else if (isTCRYPT(cd->type)) {
		if (!name)
			return 0;
		r = TCRYPT_activate(cd, name, &cd->u.tcrypt.hdr,
				    &cd->u.tcrypt.params, flags);
	} else if (isINTEGRITY(cd->type)) {
		if (!name)
			return 0;
		if (volume_key) {
			vk = crypt_alloc_volume_key(volume_key_size, volume_key);
			if (!vk)
				return -ENOMEM;
		}
		r = INTEGRITY_activate(cd, name, &cd->u.integrity.params, vk,
				       cd->u.integrity.journal_crypt_key,
				       cd->u.integrity.journal_mac_key, flags,
				       cd->u.integrity.sb_flags);
	} else if (isBITLK(cd->type)) {
		r = BITLK_activate_by_volume_key(cd, name, volume_key, volume_key_size,
						 &cd->u.bitlk.params, flags);
	} else {
		log_err(cd, _("Device type is not properly initialized."));
		r = -EINVAL;
	}

	if (r < 0)
		crypt_drop_keyring_key(cd, vk);
	crypt_free_volume_key(vk);

	return r;
}