Makefile: Add security compiling option (RELRO, SC, and FORTIFY)
[platform/upstream/cryptsetup.git] / tests / luks2-reencryption-mangle-test
1 #!/bin/bash
2
3 PS4='$LINENO:'
4 [ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".."
5 CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup
6 CRYPTSETUP_RAW=$CRYPTSETUP
7
8 CRYPTSETUP_VALGRIND=../.libs/cryptsetup
9 CRYPTSETUP_LIB_VALGRIND=../.libs
10 IMG=reenc-mangle-data
11 IMG_HDR=$IMG.hdr
12 IMG_HDR_BCP=$IMG_HDR.bcp
13 IMG_JSON=$IMG.json
14 KEY1=key1
15 DEV_NAME=reenc3492834
16
17 FAST_PBKDF2="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
18 CS_PWPARAMS="--disable-keyring --key-file $KEY1"
19 CS_PARAMS="-q --disable-locks $CS_PWPARAMS"
20 JSON_MSIZE=16384
21
22 function remove_mapping()
23 {
24         [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove --retry $DEV_NAME
25         rm -f $IMG $IMG_HDR $IMG_HDR_BCP $IMG_JSON $KEY1 >/dev/null 2>&1
26 }
27
28 function fail()
29 {
30         local frame=0
31         [ -n "$1" ] && echo "$1"
32         echo "FAILED backtrace:"
33         while caller $frame; do ((frame++)); done
34         remove_mapping
35         exit 2
36 }
37
38 function skip()
39 {
40         [ -n "$1" ] && echo "$1"
41         remove_mapping
42         exit 77
43 }
44
45 function bin_check()
46 {
47         command -v $1 >/dev/null || skip "WARNING: test require $1 binary, test skipped."
48 }
49
50 function img_json_save()
51 {
52         local _hdr=$IMG
53         [ -z "$1" ] || _hdr="$1"
54         # FIXME: why --json-file cannot be used?
55         $CRYPTSETUP luksDump --dump-json-metadata $_hdr | jq -c -M . | tr -d '\n' >$IMG_JSON
56 }
57
58 function img_json_dump()
59 {
60         img_json_save
61         jq . $IMG_JSON
62 }
63
64 function img_hash_save()
65 {
66         IMG_HASH=$(sha256sum $IMG | cut -d' ' -f 1)
67 }
68
69 function img_hash_unchanged()
70 {
71         local IMG_HASH2=$(sha256sum $IMG | cut -d' ' -f 1)
72         [ "$IMG_HASH" != "$IMG_HASH2" ] && fail "Image changed!"
73 }
74
75 function img_prepare_raw() # $1 options
76 {
77         remove_mapping
78
79         if [ ! -e $KEY1 ]; then
80                 dd if=/dev/urandom of=$KEY1 count=1 bs=32 >/dev/null 2>&1
81         fi
82
83         truncate -s 32M $IMG || fail
84         $CRYPTSETUP luksFormat $FAST_PBKDF2 $CS_PARAMS --luks2-metadata-size $JSON_MSIZE $IMG $1 || fail
85 }
86
87 function img_prepare() # $1 options
88 {
89         img_prepare_raw
90         $CRYPTSETUP reencrypt $IMG $CS_PARAMS -q --init-only --resilience none $1 >/dev/null 2>&1
91         [ $? -ne 0 ] && skip "Reencryption unsupported, test skipped."
92         img_json_save
93         img_hash_save
94 }
95
96 function _dd()
97 {
98         dd $@ status=none conv=notrunc bs=1
99 }
100
101 # header mangle functions
102 function img_update_json()
103 {
104         local _hdr="$IMG"
105         local LUKS2_BIN1_OFFSET=448
106         local LUKS2_BIN2_OFFSET=$((LUKS2_BIN1_OFFSET + $JSON_MSIZE))
107         local LUKS2_JSON_SIZE=$(($JSON_MSIZE - 4096))
108
109         # if present jq script, mangle JSON
110         if [ -n "$1" ]; then
111                 local JSON=$(cat $IMG_JSON)
112                 echo $JSON | jq -M -c "$1" >$IMG_JSON || fail
113                 local JSON=$(cat $IMG_JSON)
114                 echo $JSON | tr -d '\n' >$IMG_JSON || fail
115         fi
116
117         [ -z "$2" ] || _hdr="$2"
118
119         # wipe JSON areas
120         _dd if=/dev/zero of=$_hdr count=$LUKS2_JSON_SIZE seek=4096
121         _dd if=/dev/zero of=$_hdr count=$LUKS2_JSON_SIZE seek=$(($JSON_MSIZE + 4096))
122
123         # write JSON data
124         _dd if=$IMG_JSON of=$_hdr count=$LUKS2_JSON_SIZE seek=4096
125         _dd if=$IMG_JSON of=$_hdr count=$LUKS2_JSON_SIZE seek=$(($JSON_MSIZE + 4096))
126
127         # erase sha256 checksums
128         _dd if=/dev/zero of=$_hdr count=64 seek=$LUKS2_BIN1_OFFSET
129         _dd if=/dev/zero of=$_hdr count=64 seek=$LUKS2_BIN2_OFFSET
130
131         # calculate sha256 and write chexksums
132         local SUM1_HEX=$(_dd if=$_hdr count=$JSON_MSIZE | sha256sum | cut -d ' ' -f 1)
133         echo $SUM1_HEX | xxd -r -p | _dd of=$_hdr seek=$LUKS2_BIN1_OFFSET count=64 || fail
134
135         local SUM2_HEX=$(_dd if=$_hdr skip=$JSON_MSIZE count=$JSON_MSIZE | sha256sum | cut -d ' ' -f 1)
136         echo $SUM2_HEX | xxd -r -p | _dd of=$_hdr seek=$LUKS2_BIN2_OFFSET count=64 || fail
137
138         img_hash_save
139 }
140
141 function img_check_ok()
142 {
143         if [ $(id -u) == 0 ]; then
144                 $CRYPTSETUP open $CS_PWPARAMS $IMG $DEV_NAME || fail
145                 $CRYPTSETUP close $DEV_NAME || fail
146         fi
147
148         $CRYPTSETUP repair $IMG $CS_PARAMS || fail
149 }
150
151 function img_check_dump_ok()
152 {
153         $CRYPTSETUP luksDump $IMG >/dev/null || fail
154         img_check_fail
155 }
156
157 function img_check_fail()
158 {
159         if [ $(id -u) == 0 ]; then
160                 $CRYPTSETUP open $CS_PWPARAMS $IMG $DEV_NAME 2>/dev/null && fail
161         fi
162
163         $CRYPTSETUP repair $IMG $CS_PARAMS 2>/dev/null && fail
164         img_hash_unchanged
165 }
166
167 function img_run_reenc_ok()
168 {
169         $CRYPTSETUP_RAW reencrypt $IMG $CS_PWPARAMS -q --disable-locks --force-offline-reencrypt --resilience none || fail
170 }
171
172 function img_run_reenc_ok_data_shift()
173 {
174         $CRYPTSETUP_RAW reencrypt $IMG $CS_PWPARAMS -q --disable-locks --force-offline-reencrypt || fail
175 }
176
177 function img_run_reenc_fail()
178 {
179 $CRYPTSETUP_RAW reencrypt $IMG $CS_PWPARAMS --force-offline-reencrypt --disable-locks -q 2>/dev/null && fail "Reencryption passed (should have failed)."
180 img_hash_unchanged
181 }
182
183 function img_check_fail_repair()
184 {
185         if [ $(id -u) == 0 ]; then
186                 $CRYPTSETUP open $CS_PWPARAMS $IMG $DEV_NAME 2>/dev/null && fail
187         fi
188
189         img_run_reenc_fail
190
191         # repair metadata
192         $CRYPTSETUP repair $IMG $CS_PARAMS || fail
193
194         img_check_ok
195 }
196
197 function img_check_fail_repair_ok()
198 {
199         img_check_fail_repair
200         img_run_reenc_ok
201 }
202
203 function img_check_fail_repair_ok_data_shift()
204 {
205         img_check_fail_repair
206         img_run_reenc_ok_data_shift
207 }
208
209 function valgrind_setup()
210 {
211         bin_check valgrind
212         [ ! -f $CRYPTSETUP_VALGRIND ] && fail "Unable to get location of cryptsetup executable."
213         export LD_LIBRARY_PATH="$CRYPTSETUP_LIB_VALGRIND:$LD_LIBRARY_PATH"
214         CRYPTSETUP=valgrind_run
215         CRYPTSETUP_RAW="./valg.sh ${CRYPTSETUP_VALGRIND}"
216 }
217
218 function valgrind_run()
219 {
220         export INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}"
221         $CRYPTSETUP_RAW "$@"
222 }
223
224 [ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
225
226 bin_check jq
227 bin_check sha256sum
228 bin_check xxd
229
230 export LANG=C
231
232 [ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
233
234 echo "[1] Reencryption with old flag is rejected"
235 img_prepare
236 img_update_json '.config.requirements.mandatory = ["online-reencryptx"]'
237 img_check_fail
238 img_update_json '.config.requirements.mandatory = ["online-reencrypt-v2"]'
239 img_check_ok
240 img_run_reenc_ok
241 img_check_ok
242
243 # Simulate old reencryption with no digest (repairable)
244 img_prepare
245 img_update_json 'del(.digests."2") | .config.requirements.mandatory = ["online-reencrypt"]'
246 img_check_fail_repair_ok
247
248 # Simulate future version of reencrypt flag (should pass luksDump)
249 img_prepare
250 img_update_json '.config.requirements.mandatory = ["online-reencrypt-v999"]'
251 img_check_dump_ok
252
253 # Multiple reencrypt requirement flags makes LUKS2 invalid
254 img_prepare
255 img_update_json '.config.requirements.mandatory = .config.requirements.mandatory + ["online-reencrypt-v999"]'
256 img_check_fail
257
258 img_prepare
259 img_update_json '.config.requirements.mandatory = .config.requirements.mandatory + ["online-reencrypt"]'
260 img_check_fail
261
262 # just regular unknown requirement
263 img_prepare
264 img_update_json '.config.requirements.mandatory = .config.requirements.mandatory + ["online-reencrypt-v3X"]'
265 img_check_dump_ok
266
267 # This must fail for new releases
268 echo "[2] Old reencryption in-progress (journal)"
269 img_prepare
270 img_update_json '
271         del(.digests."2") |
272         .keyslots."2".area.type = "journal" |
273         .segments = {
274                 "0" : (.segments."0" +
275                         {"size" : .keyslots."2".area.size} +
276                         {"flags" : ["in-reencryption"]}),
277                 "1" : (.segments."0" +
278                         {"offset" : ((.segments."0".offset|tonumber) +
279                         (.keyslots."2".area.size|tonumber))|tostring}),
280                 "2" : .segments."1",
281                 "3" : .segments."2"
282         } |
283         .digests."0".segments = ["1","2"] |
284         .digests."1".segments = ["0","3"] |
285         .config.requirements.mandatory = ["online-reencrypt"]'
286 img_check_fail_repair_ok
287
288 echo "[3] Old reencryption in-progress (checksum)"
289 img_prepare
290 img_update_json '
291         del(.digests."2") |
292         .keyslots."2".area.type = "checksum" |
293         .keyslots."2".area.hash = "sha256" |
294         .keyslots."2".area.sector_size = 4096 |
295         .segments = {
296                 "0" : (.segments."0" +
297                         {"size" : .keyslots."2".area.size} +
298                         {"flags" : ["in-reencryption"]}),
299                 "1" : (.segments."0" +
300                         {"offset": ((.segments."0".offset|tonumber) +
301                         (.keyslots."2".area.size|tonumber))|tostring}),
302                 "2" : .segments."1",
303                 "3" : .segments."2"
304         } |
305         .digests."0".segments = ["1","2"] |
306         .digests."1".segments = ["0","3"] |
307         .config.requirements.mandatory = ["online-reencrypt"]'
308 img_check_fail_repair_ok
309
310 # Note: older tools cannot create this from commandline
311 echo "[4] Old decryption in-progress (journal)"
312 img_prepare
313 img_update_json '
314         del(.digests."1") |
315         del(.digests."2") |
316         del(.keyslots."1") |
317         .keyslots."2".mode = "decrypt" |
318         .keyslots."2".area.type = "journal" |
319         .segments = {
320                 "0" : {
321                         "type" : "linear",
322                         "offset" : .segments."0".offset,
323                         "size" : .keyslots."2".area.size,
324                         "flags" : ["in-reencryption"]
325                 },
326                 "1" : (.segments."0" +
327                         {"offset" : ((.segments."0".offset|tonumber) +
328                         (.keyslots."2".area.size|tonumber))|tostring}),
329                 "2" : .segments."1",
330                 "3" : {
331                         "type" : "linear",
332                         "offset" : .segments."0".offset,
333                         "size" : "dynamic",
334                         "flags" : ["backup-final"]
335                 }
336         } |
337         .digests."0".segments = ["1","2"] |
338         .config.requirements.mandatory = ["online-reencrypt"]'
339 img_check_fail_repair_ok
340
341 echo "[5] Old decryption in-progress (checksum)"
342 img_prepare
343 img_update_json '
344         del(.digests."1") |
345         del(.digests."2") |
346         del(.keyslots."1") |
347         .keyslots."2".mode = "decrypt" |
348         .keyslots."2".area.type = "checksum" |
349         .keyslots."2".area.hash = "sha256" |
350         .keyslots."2".area.sector_size = 4096 |
351         .segments = {
352                 "0" : {
353                         "type" : "linear",
354                         "offset" : .segments."0".offset,
355                         "size" : .keyslots."2".area.size,
356                         "flags" : ["in-reencryption"]
357                 },
358                 "1" : (.segments."0" +
359                         {"offset" : ((.segments."0".offset|tonumber) +
360                         (.keyslots."2".area.size|tonumber))|tostring}),
361                 "2" : .segments."1",
362                 "3" : {
363                         "type" : "linear",
364                         "offset" : .segments."0".offset,
365                         "size" : "dynamic",
366                         "flags" : ["backup-final"]
367                 }
368         } |
369         .digests."0".segments = ["1","2"] |
370         .config.requirements.mandatory = ["online-reencrypt"]'
371 img_check_fail_repair_ok
372
373 # Note - offset is set to work with the old version (with a datashift bug)
374 echo "[6] Old reencryption in-progress (datashift)"
375 img_prepare
376 img_update_json '
377         del(.digests."2") |
378         .keyslots."2".direction = "backward" |
379         .keyslots."2".area.type = "datashift" |
380         .keyslots."2".area.size = "4096" |
381         .keyslots."2".area.shift_size = ((1 * 1024 * 1024)|tostring) |
382         .segments = {
383                 "0" : (.segments."0" +
384                         {"size" : ((13 * 1024 * 1024)|tostring)}),
385                 "1" : (.segments."0" +
386                         {"offset" : ((30 * 1024 * 1024)|tostring)}),
387                 "2" : .segments."1",
388                 "3" : (.segments."2" +
389                         {"offset" : ((17 * 1024 * 1024)|tostring)}),
390         } |
391         .digests."0".segments = ["0","2"] |
392         .digests."1".segments = ["1","3"] |
393         .config.requirements.mandatory = ["online-reencrypt"]'
394 img_check_fail_repair_ok_data_shift
395
396 #
397 # NEW metadata (with reenc digest)
398 #
399 echo "[7] Reencryption with various mangled metadata"
400
401 # Normal situation
402 img_prepare
403 img_run_reenc_ok
404 img_check_ok
405
406 # The same in various steps.
407 # Repair must validate not only metadata, but also reencryption digest.
408 img_prepare
409 img_update_json 'del(.digests."2")'
410 img_check_fail_repair_ok
411
412 img_prepare '--reduce-device-size 2M'
413 img_update_json '.keyslots."2".area.shift_size = ((.keyslots."2".area.shift_size|tonumber / 2)|tostring)'
414 img_check_fail
415
416 img_prepare
417 img_update_json '
418         .keyslots."2".area.type = "checksum" |
419         .keyslots."2".area.hash = "sha256" |
420         .keyslots."2".area.sector_size = 4096'
421 img_check_fail
422
423 img_prepare
424 img_update_json '.keyslots."2".area.type = "journal"'
425 img_check_fail
426
427 img_prepare
428 img_update_json '.keyslots."2".mode = "decrypt"'
429 img_check_fail
430
431 img_prepare
432 img_update_json '.keyslots."2".direction = "backward"'
433 img_check_fail
434
435 # key_size must be 1
436 img_prepare
437 img_update_json '.keyslots."2".key_size = 16'
438 img_check_fail
439
440 # Mangling segments
441 img_prepare
442 img_update_json 'del(.segments."1")'
443 img_check_fail
444
445 img_prepare
446 img_update_json '.segments."0".encryption = "aes-cbc-null"'
447 img_check_fail
448
449 img_prepare
450 img_update_json '.segments."1".encryption = "aes-cbc-null"'
451 img_check_fail
452
453 img_prepare
454 img_update_json '.segments."2".encryption = "aes-cbc-null"'
455 img_check_fail
456
457 # Mangling digests
458 img_prepare
459 img_update_json '
460         .digests."2" = .digests."0" |
461         .digests."2".keyslots = ["2"] |
462         .digests."2".segments = []'
463 img_check_fail
464
465 img_prepare
466 img_update_json '.digests."2".iterations = 1111'
467 img_check_fail
468
469 # Simulate correct progress
470 img_prepare
471 img_update_json '
472         .segments = {
473                 "0" : (.segments."0" +
474                         {"size" : ((1 * 1024 * 1024)|tostring)}),
475                 "1" : (.segments."0" +
476                         {"offset" : ((17 * 1024 * 1024)|tostring)}),
477                 "2" : .segments."1",
478                 "3" : .segments."2"
479         } |
480         .digests."0".segments = ["1","2"] |
481         .digests."1".segments = ["0","3"]'
482 img_check_ok
483
484 # Mangling keyslots
485
486 # Set reencrypt slot to non-ignore priority
487 # This should be benign, just avoid noisy messages
488 img_prepare
489 img_update_json 'del(.keyslots."2".priority)'
490 img_check_ok
491
492 # Flags
493
494 # Remove mandatory reenc flag, but keep reenc metadata
495 img_prepare
496 img_update_json '.config.requirements.mandatory = []'
497 img_check_fail
498
499 # Unknown segment flag, should be ignored
500 img_prepare
501 img_update_json '.segments."0".flags = ["dead-parrot"]'
502 img_check_ok
503
504 echo "[8] Reencryption with AEAD is not supported"
505 img_prepare_raw
506 img_json_save
507 img_update_json '
508         .segments."0".integrity = {
509                 "type" : "hmac(sha256)",
510                 "journal_encryption": "none",
511                 "journal_integrity": "none"
512         }'
513 $CRYPTSETUP reencrypt $IMG $CS_PARAMS >/dev/null 2>&1 && fail
514
515 echo "[9] Decryption with datashift"
516 img_prepare_raw
517 $CRYPTSETUP reencrypt $CS_PARAMS --decrypt --init-only --force-offline-reencrypt --resilience checksum --header $IMG_HDR $IMG || fail
518 cp $IMG_HDR $IMG_HDR_BCP
519
520 # change hash
521 img_json_save $IMG_HDR_BCP
522 img_update_json '.keyslots."1".area.hash = "sha12345"' $IMG_HDR
523 $CRYPTSETUP reencrypt --header $IMG_HDR $IMG $CS_PARAMS --force-offline-reencrypt 2>/dev/null && fail
524
525 # change sector size
526 img_json_save $IMG_HDR_BCP
527 img_update_json '.keyslots."1".area.sector_size = 1024' $IMG_HDR
528 $CRYPTSETUP reencrypt --header $IMG_HDR $IMG $CS_PARAMS --force-offline-reencrypt 2>/dev/null && fail
529
530 # replace with new resilience mode
531 img_json_save $IMG_HDR_BCP
532 img_update_json 'del(.keyslots."1".area.hash) |
533                  del(.keyslots."1".sector_size) |
534                  .keyslots."1".area.type = "datashift-journal"' $IMG_HDR
535 $CRYPTSETUP reencrypt --header $IMG_HDR $IMG $CS_PARAMS --force-offline-reencrypt 2>/dev/null && fail
536
537 # downgrade reencryption requirement
538 img_json_save $IMG_HDR_BCP
539 img_update_json '.config.requirements.mandatory = ["online-reencrypt-v2"]' $IMG_HDR
540 $CRYPTSETUP reencrypt --header $IMG_HDR $IMG $CS_PARAMS --force-offline-reencrypt 2>/dev/null && fail
541
542 # change datashift value
543 img_json_save $IMG_HDR_BCP
544 img_update_json '.keyslots."1".area.shift_size = (((.keyslots."1".area.shift_size | tonumber) - 4096) | tostring)' $IMG_HDR
545 $CRYPTSETUP reencrypt --header $IMG_HDR $IMG $CS_PARAMS --force-offline-reencrypt 2>/dev/null && fail
546
547 remove_mapping
548 exit 0