# Copyright 2019 syzkaller project authors. All rights reserved.
# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

# Reference: https://www.kernel.org/doc/html/latest/filesystems/fscrypt.html#user-api

include <uapi/linux/fs.h>
include <uapi/linux/fscrypt.h>

ioctl$FS_IOC_SET_ENCRYPTION_POLICY(fd fd_dir, cmd const[FS_IOC_SET_ENCRYPTION_POLICY], arg ptr[in, fscrypt_policy])
ioctl$FS_IOC_GET_ENCRYPTION_PWSALT(fd fd_dir, cmd const[FS_IOC_GET_ENCRYPTION_PWSALT], arg ptr[out, array[int8, 16]])
ioctl$FS_IOC_GET_ENCRYPTION_POLICY(fd fd, cmd const[FS_IOC_GET_ENCRYPTION_POLICY], arg ptr[out, fscrypt_policy_v1])
ioctl$FS_IOC_GET_ENCRYPTION_POLICY_EX(fd fd, cmd const[FS_IOC_GET_ENCRYPTION_POLICY_EX], arg ptr[inout, fscrypt_get_policy_ex_arg])
ioctl$FS_IOC_ADD_ENCRYPTION_KEY(fd fd_dir, cmd const[FS_IOC_ADD_ENCRYPTION_KEY], arg ptr[inout, fscrypt_add_key_arg])
ioctl$FS_IOC_REMOVE_ENCRYPTION_KEY(fd fd_dir, cmd const[FS_IOC_REMOVE_ENCRYPTION_KEY], arg ptr[inout, fscrypt_remove_key_arg])
ioctl$FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS(fd fd_dir, cmd const[FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS], arg ptr[inout, fscrypt_remove_key_arg])
ioctl$FS_IOC_GET_ENCRYPTION_KEY_STATUS(fd fd_dir, cmd const[FS_IOC_GET_ENCRYPTION_KEY_STATUS], arg ptr[inout, fscrypt_get_key_status_arg])
ioctl$FS_IOC_GET_ENCRYPTION_NONCE(fd fd, cmd const[FS_IOC_GET_ENCRYPTION_NONCE], arg ptr[out, array[int8, 16]])

# Define the types for fscrypt raw keys, key identifiers, and key descriptors.
#
# "Key identifiers" are the new way to identify fscrypt keys.  They are 16-byte
# values that must be computed in a specific way from the raw key.  The
# FS_IOC_ADD_ENCRYPTION_KEY ioctl will do this computation and return the
# identifier to userspace, as a side effect of adding a key.  But it's unclear
# that syzkaller can understand this flow yet (as arrays can't be resources), so
# we also define some sample keys with precomputed identifiers to use.
#
# "Key descriptors" are the old way to identify fscrypt keys.  They are 8-byte
# values arbitrarily assigned by userspace.

fscrypt_raw_key [
	auto	array[int8[16:64]]
# 64-byte keys (usable with aes256, aes128, adiantum)
	a	stringnoz[`0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40`]
	b	stringnoz["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa11111111111111111111111111111111"]
# 32-byte key (usable with aes128, adiantum)
	c	stringnoz["abcdefghijklmnopqrstuvwxyz0123456"]
# 16-byte key (usable with aes128 only)
	d	stringnoz["abcdefghijklmnop"]
] [varlen]

fscrypt_key_identifier [
	auto	array[int8, FSCRYPT_KEY_IDENTIFIER_SIZE]
# These 16-byte values are computed from the raw keys listed above.
	a	stringnoz[`69b2f6edeee720cce0577937eb8a6751`]
	b	stringnoz[`912ae510a458723a839a9fad701538ac`]
	c	stringnoz[`1c2d6754b6cc7daacb599875d7faf9bb`]
	d	stringnoz[`7eb80af3f24ef086726a4cea3a154ce0`]
]

fscrypt_key_descriptor [
	auto	array[int8, FSCRYPT_KEY_DESCRIPTOR_SIZE]
# These are arbitrary 8-byte values.
	desc1	stringnoz[`0000111122223333`]
	desc2	stringnoz[`e355a76a11a1be18`]
	desc3	stringnoz[`e8dab99234bb312e`]
	desc4	stringnoz[`85baa174f0cb1142`]
]

# fscrypt_key_descriptor translated into a hex string
fscrypt_key_descriptor_hex [
	auto	array[flags[hex_chars, int8], 16]
	desc1	stringnoz["0000111122223333"]
	desc2	stringnoz["e355a76a11a1be18"]
	desc3	stringnoz["e8dab99234bb312e"]
	desc4	stringnoz["85baa174f0cb1142"]
]

# Below are the data structures needed to put keys for fscrypt_policy_v1 in the
# regular Linux keyrings via the add_key() syscall.  Note that this part of the
# API is deprecated in favor of FS_IOC_ADD_ENCRYPTION_KEY and fscrypt_policy_v2.

fscrypt_v1_key_description {
	prefix		stringnoz["fscrypt:"]
	descriptor	fscrypt_key_descriptor_hex
	nil		const[0, int8]
}

fscrypt_v1_key_payload {
	mode	const[0, int32]
# This really should be fscrypt_raw_key, but that has the varlen attribute so it
# doesn't work in this struct.
	raw	array[int8, 64]
	size	int32[16:64]
}

# Structs for the ioctls that get and set encryption policies

fscrypt_policy_v1 {
	version			const[0, int8]
	modes			fscrypt_policy_modes
	flags			flags[fscrypt_policy_flags, int8]
	master_key_descriptor	fscrypt_key_descriptor
}

fscrypt_policy_v2 {
	version			const[2, int8]
	modes			fscrypt_policy_modes
	flags			flags[fscrypt_policy_flags, int8]
	reserved		array[const[0, int8], 4]
	master_key_identifier	fscrypt_key_identifier
}

fscrypt_policy [
	v1	fscrypt_policy_v1
	v2	fscrypt_policy_v2
] [varlen]

fscrypt_policy_modes [
	aes256		fscrypt_policy_mode_t[FSCRYPT_MODE_AES_256_XTS, FSCRYPT_MODE_AES_256_CTS]
	aes128		fscrypt_policy_mode_t[FSCRYPT_MODE_AES_128_CBC, FSCRYPT_MODE_AES_128_CTS]
	adiantum	fscrypt_policy_mode_t[FSCRYPT_MODE_ADIANTUM, FSCRYPT_MODE_ADIANTUM]
]

type fscrypt_policy_mode_t[CONTENTS, FILENAMES] {
	contents_encryption_mode	const[CONTENTS, int8]
	filenames_encryption_mode	const[FILENAMES, int8]
}

fscrypt_policy_flags = FSCRYPT_POLICY_FLAGS_PAD_4, FSCRYPT_POLICY_FLAGS_PAD_8, FSCRYPT_POLICY_FLAGS_PAD_16, FSCRYPT_POLICY_FLAGS_PAD_32, FSCRYPT_POLICY_FLAG_DIRECT_KEY, FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64, FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32

fscrypt_get_policy_ex_arg {
	policy_size	len[policy, int64]	(in)
	policy		array[const[0, int8]]	(out)
}

# Structs for the ioctls that manage keys in the filesystem-level keyring

fscrypt_key_specifier [
	desc	fscrypt_key_specifier__by_descriptor
	id	fscrypt_key_specifier__by_identifier
]

fscrypt_key_specifier__by_descriptor {
	type		const[FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR, int32]
	reserved	const[0, int32]
	descriptor	fscrypt_key_descriptor
	reserved2	array[const[0, int8], 24]
}

fscrypt_key_specifier__by_identifier {
	type		const[FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER, int32]
	reserved	const[0, int32]
	identifier	fscrypt_key_identifier
	reserved2	array[const[0, int8], 16]
}

# Usually, fscrypt_key_specifier is entirely an input.  But in
# FS_IOC_ADD_ENCRYPTION_KEY, if 'fscrypt_add_key_arg::key_spec.type' is set to
# FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER, then
# 'fscrypt_add_key_arg::key_spec.identifier' is an output field.  The version of
# fscrypt_key_specifier below has per-field directions set to represent this.
fscrypt_key_specifier__for_add_key [
	desc	fscrypt_key_specifier__by_descriptor
	id	fscrypt_key_specifier__by_identifier_for_add_key
]

fscrypt_key_specifier__by_identifier_for_add_key {
	type		const[FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER, int32]	(in)
	reserved	const[0, int32]	(in)
	identifier	fscrypt_key_identifier	(out)
	reserved2	array[const[0, int8], 16]	(in)
}

fscrypt_key_specifier_type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR, FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER

fscrypt_provisioning_key_payload {
	type		flags[fscrypt_key_specifier_type, int32]
	reserved	const[0, int32]
	raw		fscrypt_raw_key
}

fscrypt_add_key_arg {
	key_spec	fscrypt_key_specifier__for_add_key
	raw_size	bytesize[raw, int32]	(in)
	key_id		fscrypt_provisioning_key[opt]	(in)
	reserved	array[const[0, int32], 8]	(in)
	raw		fscrypt_raw_key	(in)
}

fscrypt_remove_key_arg {
	key_spec		fscrypt_key_specifier	(in)
	removal_status_flags	int32	(out)
	reserved		array[const[0, int32], 5]	(in)
}

fscrypt_get_key_status_arg {
	key_spec	fscrypt_key_specifier	(in)
	reserved	array[const[0, int32], 6]	(in)

	status		int32	(out)
	status_flags	int32	(out)
	user_count	int32	(out)
	out_reserved	array[const[0, int32], 13]	(out)
}
