98a407ce31f42728b13999b1b2b17879bb74d8fa
[xfstests-dev.git] / common / encrypt
1 ##/bin/bash
2 # SPDX-License-Identifier: GPL-2.0
3 # Copyright (c) 2016 Google, Inc.  All Rights Reserved.
4 #
5 # Functions for setting up and testing file encryption
6
7 #
8 # _require_scratch_encryption [-c CONTENTS_MODE] [-n FILENAMES_MODE]
9 #                             [-f POLICY_FLAGS] [-v POLICY_VERSION]
10 #
11 # Require encryption support on the scratch device.
12 #
13 # This checks for support for the default type of encryption policy (v1 with
14 # AES-256-XTS and AES-256-CTS).  Options can be specified to also require
15 # support for a different type of encryption policy.
16 #
17 _require_scratch_encryption()
18 {
19         _require_scratch
20
21         _require_xfs_io_command "set_encpolicy"
22
23         # The 'test_dummy_encryption' mount option interferes with trying to use
24         # encryption for real, even if we are just trying to get/set policies
25         # and never put any keys in the keyring.  So skip the real encryption
26         # tests if the 'test_dummy_encryption' mount option was specified.
27         _exclude_scratch_mount_option "test_dummy_encryption"
28
29         # Make a filesystem on the scratch device with the encryption feature
30         # enabled.  If this fails then probably the userspace tools (e.g.
31         # e2fsprogs or f2fs-tools) are too old to understand encryption.
32         if ! _scratch_mkfs_encrypted &>>$seqres.full; then
33                 _notrun "$FSTYP userspace tools do not support encryption"
34         fi
35
36         # Try to mount the filesystem.  If this fails then either the kernel
37         # isn't aware of encryption, or the mkfs options were not compatible
38         # with encryption (e.g. ext4 with block size != PAGE_SIZE).
39         if ! _try_scratch_mount &>>$seqres.full; then
40                 _notrun "kernel is unaware of $FSTYP encryption feature," \
41                         "or mkfs options are not compatible with encryption"
42         fi
43
44         # The kernel may be aware of encryption without supporting it.  For
45         # example, for ext4 this is the case with kernels configured with
46         # CONFIG_EXT4_FS_ENCRYPTION=n.  Detect support for encryption by trying
47         # to set an encryption policy.  (For ext4 we could instead check for the
48         # presence of /sys/fs/ext4/features/encryption, but this is broken on
49         # some older kernels and is ext4-specific anyway.)
50         mkdir $SCRATCH_MNT/tmpdir
51         if _set_encpolicy $SCRATCH_MNT/tmpdir 2>&1 >>$seqres.full | \
52                 egrep -q 'Inappropriate ioctl for device|Operation not supported'
53         then
54                 _notrun "kernel does not support $FSTYP encryption"
55         fi
56         rmdir $SCRATCH_MNT/tmpdir
57
58         # If required, check for support for the specific type of encryption
59         # policy required by the test.
60         if [ $# -ne 0 ]; then
61                 _require_encryption_policy_support $SCRATCH_MNT "$@"
62         fi
63
64         _scratch_unmount
65 }
66
67 _require_encryption_policy_support()
68 {
69         local mnt=$1
70         local dir=$mnt/tmpdir
71         local set_encpolicy_args=""
72         local policy_flags=0
73         local policy_version=1
74         local c
75
76         OPTIND=2
77         while getopts "c:n:f:v:" c; do
78                 case $c in
79                 c|n)
80                         set_encpolicy_args+=" -$c $OPTARG"
81                         ;;
82                 f)
83                         set_encpolicy_args+=" -$c $OPTARG"
84                         policy_flags=$OPTARG
85                         ;;
86                 v)
87                         set_encpolicy_args+=" -$c $OPTARG"
88                         policy_version=$OPTARG
89                         ;;
90                 *)
91                         _fail "Unrecognized option '$c'"
92                         ;;
93                 esac
94         done
95         set_encpolicy_args=${set_encpolicy_args# }
96
97         echo "Checking whether kernel supports encryption policy: $set_encpolicy_args" \
98                 >> $seqres.full
99
100         if (( policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 )); then
101                 _scratch_unmount
102                 _scratch_mkfs_stable_inodes_encrypted &>> $seqres.full
103                 _scratch_mount
104         fi
105
106         mkdir $dir
107         if (( policy_version > 1 )); then
108                 _require_xfs_io_command "get_encpolicy" "-t"
109                 local output=$(_get_encpolicy $dir -t)
110                 if [ "$output" != "supported" ]; then
111                         if [ "$output" = "unsupported" ]; then
112                                 _notrun "kernel does not support $FSTYP encryption v2 API"
113                         fi
114                         _fail "Unexpected output from 'get_encpolicy -t': $output"
115                 fi
116                 # Both the kernel and xfs_io support v2 encryption policies, and
117                 # therefore also filesystem-level keys -- since that's the only
118                 # way to provide keys for v2 policies.
119                 local raw_key=$(_generate_raw_encryption_key)
120                 local keyspec=$(_add_enckey $mnt "$raw_key" | awk '{print $NF}')
121         else
122                 _require_command "$KEYCTL_PROG" keyctl
123                 _new_session_keyring
124                 local keyspec=$(_generate_session_encryption_key)
125         fi
126         if _set_encpolicy $dir $keyspec $set_encpolicy_args \
127                 2>&1 >>$seqres.full | egrep -q 'Invalid argument'; then
128                 _notrun "kernel does not support encryption policy: '$set_encpolicy_args'"
129         fi
130         # fscrypt allows setting policies with modes it knows about, even
131         # without kernel crypto API support.  E.g. a policy using Adiantum
132         # encryption can be set on a kernel without CONFIG_CRYPTO_ADIANTUM.
133         # But actually trying to use such an encrypted directory will fail.
134         # To reliably check for availability of both the contents and filenames
135         # encryption modes, try creating a nonempty file.
136         if ! echo foo > $dir/file; then
137                 _notrun "encryption policy '$set_encpolicy_args' is unusable; probably missing kernel crypto API support"
138         fi
139         if (( policy_version <= 1 )); then
140                 $KEYCTL_PROG clear @s
141         fi
142         rm -r $dir
143 }
144
145 _scratch_mkfs_encrypted()
146 {
147         case $FSTYP in
148         ext4|f2fs)
149                 _scratch_mkfs -O encrypt
150                 ;;
151         ubifs)
152                 # erase the UBI volume; reformated automatically on next mount
153                 $UBIUPDATEVOL_PROG ${SCRATCH_DEV} -t
154                 ;;
155         *)
156                 _notrun "No encryption support for $FSTYP"
157                 ;;
158         esac
159 }
160
161 _scratch_mkfs_sized_encrypted()
162 {
163         case $FSTYP in
164         ext4|f2fs)
165                 MKFS_OPTIONS="$MKFS_OPTIONS -O encrypt" _scratch_mkfs_sized $*
166                 ;;
167         *)
168                 _notrun "Filesystem $FSTYP not supported in _scratch_mkfs_sized_encrypted"
169                 ;;
170         esac
171 }
172
173 # Like _scratch_mkfs_encrypted(), but add -O stable_inodes if applicable for the
174 # filesystem type.  This is necessary for using encryption policies that include
175 # the inode number in the IVs, e.g. policies with the IV_INO_LBLK_64 flag set.
176 _scratch_mkfs_stable_inodes_encrypted()
177 {
178         case $FSTYP in
179         ext4)
180                 if ! _scratch_mkfs -O encrypt -O stable_inodes; then
181                         _notrun "-O stable_inodes is not supported"
182                 fi
183                 ;;
184         *)
185                 _scratch_mkfs_encrypted
186                 ;;
187         esac
188 }
189
190 # For some tests it's helpful to always use the same key so that the test's
191 # output is always the same.  For this purpose the following key can be used:
192 TEST_RAW_KEY=
193 for i in {1..64}; do
194         TEST_RAW_KEY+="\\x$(printf "%02x" $i)"
195 done
196 # Key descriptor: arbitrary value
197 TEST_KEY_DESCRIPTOR="0000111122223333"
198 # Key identifier: HKDF-SHA512(key=$TEST_RAW_KEY, salt="", info="fscrypt\0\x01")
199 TEST_KEY_IDENTIFIER="69b2f6edeee720cce0577937eb8a6751"
200
201 # Give the invoking shell a new session keyring.  This makes any keys we add to
202 # the session keyring scoped to the lifetime of the test script.
203 _new_session_keyring()
204 {
205         $KEYCTL_PROG new_session >>$seqres.full
206 }
207
208 # Generate a key descriptor (16 character hex string)
209 _generate_key_descriptor()
210 {
211         local keydesc=""
212         local i
213         for ((i = 0; i < 8; i++)); do
214                 keydesc="${keydesc}$(printf "%02x" $(( $RANDOM % 256 )))"
215         done
216         echo $keydesc
217 }
218
219 # Generate a raw encryption key, but don't add it to any keyring yet.
220 _generate_raw_encryption_key()
221 {
222         local raw=""
223         local i
224         for ((i = 0; i < 64; i++)); do
225                 raw="${raw}\\x$(printf "%02x" $(( $RANDOM % 256 )))"
226         done
227         echo $raw
228 }
229
230 # Add the specified raw encryption key to the session keyring, using the
231 # specified key descriptor.
232 _add_session_encryption_key()
233 {
234         local keydesc=$1
235         local raw=$2
236
237         #
238         # Add the key to the session keyring.  The required structure is:
239         #
240         #       #define FS_MAX_KEY_SIZE 64
241         #       struct fscrypt_key {
242         #               u32 mode;
243         #               u8 raw[FS_MAX_KEY_SIZE];
244         #               u32 size;
245         #       } __packed;
246         #
247         # The kernel ignores 'mode' but requires that 'size' be 64.
248         #
249         # Keys are named $FSTYP:KEYDESC where KEYDESC is the 16-character key
250         # descriptor hex string.  Newer kernels (ext4 4.8 and later, f2fs 4.6
251         # and later) also allow the common key prefix "fscrypt:" in addition to
252         # their filesystem-specific key prefix ("ext4:", "f2fs:").  It would be
253         # nice to use the common key prefix, but for now use the filesystem-
254         # specific prefix to make it possible to test older kernels...
255         #
256         local big_endian=$(echo -ne '\x11' | od -tx2 | head -1 | \
257                            cut -f2 -d' ' | cut -c1 )
258         if (( big_endian )); then
259                 local mode='\x00\x00\x00\x00'
260                 local size='\x00\x00\x00\x40'
261         else
262                 local mode='\x00\x00\x00\x00'
263                 local size='\x40\x00\x00\x00'
264         fi
265         echo -n -e "${mode}${raw}${size}" |
266                 $KEYCTL_PROG padd logon $FSTYP:$keydesc @s >>$seqres.full
267 }
268
269 #
270 # Generate a random encryption key, add it to the session keyring, and print out
271 # the resulting key descriptor (example: "8bf798e1a494e1ec").  Requires the
272 # keyctl program.  It's assumed the caller has already set up a test-scoped
273 # session keyring using _new_session_keyring.
274 #
275 _generate_session_encryption_key()
276 {
277         local keydesc=$(_generate_key_descriptor)
278         local raw=$(_generate_raw_encryption_key)
279
280         _add_session_encryption_key $keydesc $raw
281
282         echo $keydesc
283 }
284
285 # Unlink an encryption key from the session keyring, given its key descriptor.
286 _unlink_session_encryption_key()
287 {
288         local keydesc=$1
289         local keyid=$($KEYCTL_PROG search @s logon $FSTYP:$keydesc)
290         $KEYCTL_PROG unlink $keyid >>$seqres.full
291 }
292
293 # Revoke an encryption key from the session keyring, given its key descriptor.
294 _revoke_session_encryption_key()
295 {
296         local keydesc=$1
297         local keyid=$($KEYCTL_PROG search @s logon $FSTYP:$keydesc)
298         $KEYCTL_PROG revoke $keyid >>$seqres.full
299 }
300
301 # Set an encryption policy on the specified directory.
302 _set_encpolicy()
303 {
304         local dir=$1
305         shift
306
307         $XFS_IO_PROG -c "set_encpolicy $*" "$dir"
308 }
309
310 _user_do_set_encpolicy()
311 {
312         local dir=$1
313         shift
314
315         _user_do "$XFS_IO_PROG -c \"set_encpolicy $*\" \"$dir\""
316 }
317
318 # Display the specified file or directory's encryption policy.
319 _get_encpolicy()
320 {
321         local file=$1
322         shift
323
324         $XFS_IO_PROG -c "get_encpolicy $*" "$file"
325 }
326
327 _user_do_get_encpolicy()
328 {
329         local file=$1
330         shift
331
332         _user_do "$XFS_IO_PROG -c \"get_encpolicy $*\" \"$file\""
333 }
334
335 # Add an encryption key to the given filesystem.
336 _add_enckey()
337 {
338         local mnt=$1
339         local raw_key=$2
340         shift 2
341
342         echo -ne "$raw_key" | $XFS_IO_PROG -c "add_enckey $*" "$mnt"
343 }
344
345 _user_do_add_enckey()
346 {
347         local mnt=$1
348         local raw_key=$2
349         shift 2
350
351         _user_do "echo -ne \"$raw_key\" | $XFS_IO_PROG -c \"add_enckey $*\" \"$mnt\""
352 }
353
354 # Remove the given encryption key from the given filesystem.
355 _rm_enckey()
356 {
357         local mnt=$1
358         local keyspec=$2
359         shift 2
360
361         $XFS_IO_PROG -c "rm_enckey $* $keyspec" "$mnt"
362 }
363
364 _user_do_rm_enckey()
365 {
366         local mnt=$1
367         local keyspec=$2
368         shift 2
369
370         _user_do "$XFS_IO_PROG -c \"rm_enckey $* $keyspec\" \"$mnt\""
371 }
372
373 # Get the status of the given encryption key on the given filesystem.
374 _enckey_status()
375 {
376         local mnt=$1
377         local keyspec=$2
378         shift 2
379
380         $XFS_IO_PROG -c "enckey_status $* $keyspec" "$mnt"
381 }
382
383 _user_do_enckey_status()
384 {
385         local mnt=$1
386         local keyspec=$2
387         shift 2
388
389         _user_do "$XFS_IO_PROG -c \"enckey_status $* $keyspec\" \"$mnt\""
390 }
391
392 # Retrieve the encryption nonce of the given inode as a hex string.  The nonce
393 # was randomly generated by the filesystem and isn't exposed directly to
394 # userspace.  But it can be read using the filesystem's debugging tools.
395 _get_encryption_nonce()
396 {
397         local device=$1
398         local inode=$2
399
400         case $FSTYP in
401         ext4)
402                 # Use debugfs to dump the special xattr named "c", which is the
403                 # file's fscrypt_context.  This produces a line like:
404                 #
405                 #       c (28) = 01 01 04 02 00 00 00 00 00 00 00 00 ef bd 18 76 5d f6 41 4e c0 a2 cd 5f 91 29 7e 12
406                 #
407                 # Then filter it to get just the 16-byte 'nonce' field at the end:
408                 #
409                 #       efbd18765df6414ec0a2cd5f91297e12
410                 #
411                 $DEBUGFS_PROG $device -R "ea_get <$inode> c" 2>>$seqres.full \
412                         | grep '^c ' | sed 's/^.*=//' | tr -d ' \n' | tail -c 32
413                 ;;
414         f2fs)
415                 # dump.f2fs prints the fscrypt_context like:
416                 #
417                 #       xattr: e_name_index:9 e_name:c e_name_len:1 e_value_size:28 e_value:
418                 #       format: 1
419                 #       contents_encryption_mode: 0x1
420                 #       filenames_encryption_mode: 0x4
421                 #       flags: 0x2
422                 #       master_key_descriptor: 0000000000000000
423                 #       nonce: EFBD18765DF6414EC0A2CD5F91297E12
424                 #
425                 # Also support the case where the whole xattr is printed as hex,
426                 # as is the case for fscrypt_context_v2.
427                 #
428                 #       xattr: e_name_index:9 e_name:c e_name_len:1 e_value_size:40 e_value:
429                 #       020104020000000033809BFEBE68A4AD264079B30861DD5E6B9E72D07523C58794ACF52534BAA756
430                 #
431                 $DUMP_F2FS_PROG -i $inode $device | awk '
432                         /\<e_name:c\>/ { found = 1 }
433                         (/^nonce:/ || /^[[:xdigit:]]+$/) && found {
434                                 print substr($0, length($0) - 31, 32);
435                                 found = 0;
436                         }'
437                 ;;
438         *)
439                 _fail "_get_encryption_nonce() isn't implemented on $FSTYP"
440                 ;;
441         esac
442 }
443
444 # Require support for _get_encryption_nonce()
445 _require_get_encryption_nonce_support()
446 {
447         echo "Checking for _get_encryption_nonce() support for $FSTYP" >> $seqres.full
448         case $FSTYP in
449         ext4)
450                 _require_command "$DEBUGFS_PROG" debugfs
451                 ;;
452         f2fs)
453                 _require_command "$DUMP_F2FS_PROG" dump.f2fs
454                 # For fscrypt_context_v2, we actually need a f2fs-tools version
455                 # that has the patch "f2fs-tools: improve xattr value printing"
456                 # (https://sourceforge.net/p/linux-f2fs/mailman/message/36648640/).
457                 # Otherwise the xattr is incorrectly parsed as v1.  But just let
458                 # the test fail in that case, as it was an f2fs-tools bug...
459                 ;;
460         *)
461                 _notrun "_get_encryption_nonce() isn't implemented on $FSTYP"
462                 ;;
463         esac
464 }
465
466 # Retrieve the encrypted filename stored on-disk for the given file.
467 # The name is printed to stdout in binary.
468 _get_ciphertext_filename()
469 {
470         local device=$1
471         local inode=$2
472         local dir_inode=$3
473
474         case $FSTYP in
475         ext4)
476                 # Extract the filename from the debugfs output line like:
477                 #
478                 #  131075  100644 (1)      0      0       0 22-Apr-2019 16:54 \xa2\x85\xb0z\x13\xe9\x09\x86R\xed\xdc\xce\xad\x14d\x19
479                 #
480                 # Bytes are shown either literally or as \xHH-style escape
481                 # sequences; we have to decode the escaped bytes here.
482                 #
483                 $DEBUGFS_PROG $device -R "ls -l -r <$dir_inode>" \
484                                         2>>$seqres.full | perl -ne '
485                         next if not /^\s*'$inode'\s+/;
486                         s/.*?\d\d:\d\d //;
487                         chomp;
488                         s/\\x([[:xdigit:]]{2})/chr hex $1/eg;
489                         print;'
490                 ;;
491         f2fs)
492                 # Extract the filename from the dump.f2fs output line like:
493                 #
494                 #  i_name                                       [UpkzIPuts9by1oDmE+Ivfw]
495                 #
496                 # The name is shown base64-encoded; we have to decode it here.
497                 #
498                 $DUMP_F2FS_PROG $device -i $inode | perl -ne '
499                         next if not /^i_name\s+\[([A-Za-z0-9+,]+)\]/;
500                         chomp $1;
501                         my @chars = split //, $1;
502                         my $ac = 0;
503                         my $bits = 0;
504                         my $table = join "", (A..Z, a..z, 0..9, "+", ",");
505                         foreach (@chars) {
506                                 $ac += index($table, $_) << $bits;
507                                 $bits += 6;
508                                 if ($bits >= 8) {
509                                         print chr($ac & 0xff);
510                                         $ac >>= 8;
511                                         $bits -= 8;
512                                 }
513                         }
514                         if ($ac != 0) {
515                                 print STDERR "Invalid base64-encoded string!\n";
516                         }'
517                 ;;
518         *)
519                 _fail "_get_ciphertext_filename() isn't implemented on $FSTYP"
520                 ;;
521         esac
522 }
523
524 # Require support for _get_ciphertext_filename().
525 _require_get_ciphertext_filename_support()
526 {
527         echo "Checking for _get_ciphertext_filename() support for $FSTYP" >> $seqres.full
528         case $FSTYP in
529         ext4)
530                 # Verify that the "ls -l -r" debugfs command is supported and
531                 # that it hex-encodes non-ASCII characters, rather than using an
532                 # ambiguous escaping method.  This requires e2fsprogs v1.45.1 or
533                 # later; or more specifically, a version that has the commit
534                 # "debugfs: avoid ambiguity when printing filenames".
535                 _require_command "$DEBUGFS_PROG" debugfs
536                 _scratch_mount
537                 touch $SCRATCH_MNT/$'\xc1'
538                 _scratch_unmount
539                 if ! $DEBUGFS_PROG $SCRATCH_DEV -R "ls -l -r /" 2>&1 \
540                         | tee -a $seqres.full | grep -E -q '\s+\\xc1\s*$'; then
541                         _notrun "debugfs (e2fsprogs) is too old; doesn't support showing unambiguous on-disk filenames"
542                 fi
543                 ;;
544         f2fs)
545                 # Verify that dump.f2fs shows encrypted filenames in full.  This
546                 # requires f2fs-tools v1.13.0 or later; or more specifically, a
547                 # version that has the commit
548                 # "f2fs-tools: improve filename printing".
549
550                 _require_command "$DUMP_F2FS_PROG" dump.f2fs
551                 _require_command "$KEYCTL_PROG" keyctl
552                 _scratch_mount
553                 _new_session_keyring
554
555                 local keydesc=$(_generate_session_encryption_key)
556                 local dir=$SCRATCH_MNT/test.${FUNCNAME[0]}
557                 local file=$dir/$(perl -e 'print "A" x 255')
558                 mkdir $dir
559                 _set_encpolicy $dir $keydesc
560                 touch $file
561                 local inode=$(stat -c %i $file)
562
563                 _scratch_unmount
564                 $KEYCTL_PROG clear @s
565
566                 # 255-character filename should result in 340 base64 characters.
567                 if ! $DUMP_F2FS_PROG -i $inode $SCRATCH_DEV \
568                         | grep -E -q '^i_name[[:space:]]+\[[A-Za-z0-9+,]{340}\]'; then
569                         _notrun "dump.f2fs (f2fs-tools) is too old; doesn't support showing unambiguous on-disk filenames"
570                 fi
571                 ;;
572         *)
573                 _notrun "_get_ciphertext_filename() isn't implemented on $FSTYP"
574                 ;;
575         esac
576 }
577
578 # Get an encrypted file's list of on-disk blocks as a comma-separated list of
579 # block offsets from the start of the device.  "Blocks" are 512 bytes each here.
580 _get_ciphertext_block_list()
581 {
582         local file=$1
583
584         sync
585         $XFS_IO_PROG -c fiemap $file | perl -ne '
586                 next if not /^\s*\d+: \[\d+\.\.\d+\]: (\d+)\.\.(\d+)/;
587                 print $_ . "," foreach $1..$2;' | sed 's/,$//'
588 }
589
590 # Dump a block list that was previously saved by _get_ciphertext_block_list().
591 _dump_ciphertext_blocks()
592 {
593         local device=$1
594         local blocklist=$2
595         local block
596
597         for block in $(tr ',' ' ' <<< $blocklist); do
598                 dd if=$device bs=512 count=1 skip=$block status=none
599         done
600 }
601
602 _do_verify_ciphertext_for_encryption_policy()
603 {
604         local contents_encryption_mode=$1
605         local filenames_encryption_mode=$2
606         local policy_flags=$3
607         local set_encpolicy_args=$4
608         local keyspec=$5
609         local raw_key_hex=$6
610         local crypt_contents_cmd="$here/src/fscrypt-crypt-util $7"
611         local crypt_filename_cmd="$here/src/fscrypt-crypt-util $8"
612
613         local blocksize=$(_get_block_size $SCRATCH_MNT)
614         local test_contents_files=()
615         local test_filenames_files=()
616         local i src dir dst inode blocklist \
617               padding_flag padding dir_inode len name f nonce decrypted_name
618
619         # Create files whose encrypted contents we'll verify.  For each, save
620         # the information: (copy of original file, inode number of encrypted
621         # file, comma-separated block list) into test_contents_files[].
622         echo "Creating files for contents verification" >> $seqres.full
623         i=1
624         rm -f $tmp.testfile_*
625         for src in /dev/zero /dev/urandom; do
626                 head -c $((4 * blocksize)) $src > $tmp.testfile_$i
627                 (( i++ ))
628         done
629         dir=$SCRATCH_MNT/encdir
630         mkdir $dir
631         _set_encpolicy $dir $keyspec $set_encpolicy_args -f $policy_flags
632         for src in $tmp.testfile_*; do
633                 dst=$dir/${src##*.}
634                 cp $src $dst
635                 inode=$(stat -c %i $dst)
636                 blocklist=$(_get_ciphertext_block_list $dst)
637                 test_contents_files+=("$src $inode $blocklist")
638         done
639
640         # Create files whose encrypted names we'll verify.  For each, save the
641         # information: (original filename, inode number of encrypted file, inode
642         # of parent directory, padding amount) into test_filenames_files[].  Try
643         # each padding amount: 4, 8, 16, or 32 bytes.  Also try various filename
644         # lengths, including boundary cases.  Assume NAME_MAX == 255.
645         echo "Creating files for filenames verification" >> $seqres.full
646         for padding_flag in 0 1 2 3; do
647                 padding=$((4 << padding_flag))
648                 dir=$SCRATCH_MNT/encdir.pad$padding
649                 mkdir $dir
650                 dir_inode=$(stat -c %i $dir)
651                 _set_encpolicy $dir $keyspec $set_encpolicy_args \
652                         -f $((policy_flags | padding_flag))
653                 for len in 1 3 15 16 17 32 100 254 255; do
654                         name=$(tr -d -C a-zA-Z0-9 < /dev/urandom | head -c $len)
655                         touch $dir/$name
656                         inode=$(stat -c %i $dir/$name)
657                         test_filenames_files+=("$name $inode $dir_inode $padding")
658                 done
659         done
660
661         # Now unmount the filesystem and verify the ciphertext we just wrote.
662         _scratch_unmount
663
664         echo "Verifying encrypted file contents" >> $seqres.full
665         for f in "${test_contents_files[@]}"; do
666                 read -r src inode blocklist <<< "$f"
667                 nonce=$(_get_encryption_nonce $SCRATCH_DEV $inode)
668                 _dump_ciphertext_blocks $SCRATCH_DEV $blocklist > $tmp.actual_contents
669                 $crypt_contents_cmd $contents_encryption_mode $raw_key_hex \
670                         --file-nonce=$nonce --block-size=$blocksize \
671                         --inode-number=$inode < $src > $tmp.expected_contents
672                 if ! cmp $tmp.expected_contents $tmp.actual_contents; then
673                         _fail "Expected encrypted contents != actual encrypted contents.  File: $f"
674                 fi
675                 $crypt_contents_cmd $contents_encryption_mode $raw_key_hex \
676                         --decrypt --file-nonce=$nonce --block-size=$blocksize \
677                         --inode-number=$inode \
678                         < $tmp.actual_contents > $tmp.decrypted_contents
679                 if ! cmp $src $tmp.decrypted_contents; then
680                         _fail "Contents decryption sanity check failed.  File: $f"
681                 fi
682         done
683
684         echo "Verifying encrypted file names" >> $seqres.full
685         for f in "${test_filenames_files[@]}"; do
686                 read -r name inode dir_inode padding <<< "$f"
687                 nonce=$(_get_encryption_nonce $SCRATCH_DEV $dir_inode)
688                 _get_ciphertext_filename $SCRATCH_DEV $inode $dir_inode \
689                         > $tmp.actual_name
690                 echo -n "$name" | \
691                         $crypt_filename_cmd $filenames_encryption_mode \
692                         $raw_key_hex --file-nonce=$nonce --padding=$padding \
693                         --block-size=255 --inode-number=$dir_inode \
694                         > $tmp.expected_name
695                 if ! cmp $tmp.expected_name $tmp.actual_name; then
696                         _fail "Expected encrypted filename != actual encrypted filename.  File: $f"
697                 fi
698                 $crypt_filename_cmd $filenames_encryption_mode $raw_key_hex \
699                         --decrypt --file-nonce=$nonce --padding=$padding \
700                         --block-size=255 --inode-number=$dir_inode \
701                         < $tmp.actual_name > $tmp.decrypted_name
702                 decrypted_name=$(tr -d '\0' < $tmp.decrypted_name)
703                 if [ "$name" != "$decrypted_name" ]; then
704                         _fail "Filename decryption sanity check failed ($name != $decrypted_name).  File: $f"
705                 fi
706         done
707 }
708
709 # fscrypt UAPI constants (see <linux/fscrypt.h>)
710
711 FSCRYPT_MODE_AES_256_XTS=1
712 FSCRYPT_MODE_AES_256_CTS=4
713 FSCRYPT_MODE_AES_128_CBC=5
714 FSCRYPT_MODE_AES_128_CTS=6
715 FSCRYPT_MODE_ADIANTUM=9
716
717 FSCRYPT_POLICY_FLAG_DIRECT_KEY=0x04
718 FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64=0x08
719
720 _fscrypt_mode_name_to_num()
721 {
722         local name=$1
723
724         case "$name" in
725         AES-256-XTS)            echo $FSCRYPT_MODE_AES_256_XTS ;;
726         AES-256-CTS-CBC)        echo $FSCRYPT_MODE_AES_256_CTS ;;
727         AES-128-CBC-ESSIV)      echo $FSCRYPT_MODE_AES_128_CBC ;;
728         AES-128-CTS-CBC)        echo $FSCRYPT_MODE_AES_128_CTS ;;
729         Adiantum)               echo $FSCRYPT_MODE_ADIANTUM ;;
730         *)                      _fail "Unknown fscrypt mode: $name" ;;
731         esac
732 }
733
734 # Verify that file contents and names are encrypted correctly when an encryption
735 # policy of the specified type is used.
736 #
737 # The first two parameters are the contents and filenames encryption modes to
738 # test.  The following optional parameters are also accepted to further modify
739 # the type of encryption policy that is tested:
740 #
741 #       'v2':                   test a v2 encryption policy
742 #       'direct':               test the DIRECT_KEY policy flag
743 #       'iv_ino_lblk_64':       test the IV_INO_LBLK_64 policy flag
744 #
745 _verify_ciphertext_for_encryption_policy()
746 {
747         local contents_encryption_mode=$1
748         local filenames_encryption_mode=$2
749         local opt
750         local policy_version=1
751         local policy_flags=0
752         local set_encpolicy_args=""
753         local crypt_util_args=""
754         local crypt_util_contents_args=""
755         local crypt_util_filename_args=""
756
757         shift 2
758         for opt; do
759                 case "$opt" in
760                 v2)
761                         policy_version=2
762                         ;;
763                 direct)
764                         if [ $contents_encryption_mode != \
765                              $filenames_encryption_mode ]; then
766                                 _fail "For direct key mode, contents and filenames modes must match"
767                         fi
768                         (( policy_flags |= FSCRYPT_POLICY_FLAG_DIRECT_KEY ))
769                         ;;
770                 iv_ino_lblk_64)
771                         (( policy_flags |= FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 ))
772                         ;;
773                 *)
774                         _fail "Unknown option '$opt' passed to ${FUNCNAME[0]}"
775                         ;;
776                 esac
777         done
778         local contents_mode_num=$(_fscrypt_mode_name_to_num $contents_encryption_mode)
779         local filenames_mode_num=$(_fscrypt_mode_name_to_num $filenames_encryption_mode)
780
781         set_encpolicy_args+=" -c $contents_mode_num"
782         set_encpolicy_args+=" -n $filenames_mode_num"
783
784         if (( policy_version > 1 )); then
785                 set_encpolicy_args+=" -v 2"
786                 crypt_util_args+=" --kdf=HKDF-SHA512"
787                 if (( policy_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY )); then
788                         if (( policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 )); then
789                                 _fail "'direct' and 'iv_ino_lblk_64' options are mutually exclusive"
790                         fi
791                         crypt_util_args+=" --mode-num=$contents_mode_num"
792                 elif (( policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 )); then
793                         crypt_util_args+=" --iv-ino-lblk-64"
794                         crypt_util_contents_args+=" --mode-num=$contents_mode_num"
795                         crypt_util_filename_args+=" --mode-num=$filenames_mode_num"
796                 fi
797         else
798                 if (( policy_flags & ~FSCRYPT_POLICY_FLAG_DIRECT_KEY )); then
799                         _fail "unsupported flags for v1 policy: $policy_flags"
800                 fi
801                 if (( policy_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY )); then
802                         crypt_util_args+=" --kdf=none"
803                 else
804                         crypt_util_args+=" --kdf=AES-128-ECB"
805                 fi
806         fi
807         set_encpolicy_args=${set_encpolicy_args# }
808
809         _require_scratch_encryption $set_encpolicy_args -f $policy_flags
810         _require_test_program "fscrypt-crypt-util"
811         _require_xfs_io_command "fiemap"
812         _require_get_encryption_nonce_support
813         _require_get_ciphertext_filename_support
814         if (( policy_version == 1 )); then
815                 _require_command "$KEYCTL_PROG" keyctl
816         fi
817
818         echo "Creating encryption-capable filesystem" >> $seqres.full
819         if (( policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 )); then
820                 _scratch_mkfs_stable_inodes_encrypted &>> $seqres.full
821         else
822                 _scratch_mkfs_encrypted &>> $seqres.full
823         fi
824         _scratch_mount
825
826         crypt_util_args+=" --fs-uuid=$(blkid -s UUID -o value $SCRATCH_DEV | tr -d -)"
827
828         crypt_util_contents_args+="$crypt_util_args"
829         crypt_util_filename_args+="$crypt_util_args"
830
831         echo "Generating encryption key" >> $seqres.full
832         local raw_key=$(_generate_raw_encryption_key)
833         if (( policy_version > 1 )); then
834                 local keyspec=$(_add_enckey $SCRATCH_MNT "$raw_key" \
835                                 | awk '{print $NF}')
836         else
837                 local keyspec=$(_generate_key_descriptor)
838                 _new_session_keyring
839                 _add_session_encryption_key $keyspec $raw_key
840         fi
841         local raw_key_hex=$(echo "$raw_key" | tr -d '\\x')
842
843         echo
844         echo -e "Verifying ciphertext with parameters:"
845         echo -e "\tcontents_encryption_mode: $contents_encryption_mode"
846         echo -e "\tfilenames_encryption_mode: $filenames_encryption_mode"
847         [ $# -ne 0 ] && echo -e "\toptions: $*"
848
849         _do_verify_ciphertext_for_encryption_policy \
850                 "$contents_encryption_mode" \
851                 "$filenames_encryption_mode" \
852                 "$policy_flags" \
853                 "$set_encpolicy_args" \
854                 "$keyspec" \
855                 "$raw_key_hex" \
856                 "$crypt_util_contents_args" \
857                 "$crypt_util_filename_args"
858 }