1#!/usr/bin/env python3

2# Copyright 2018 Canonical Ltd.

3# Written by:

4# Jonathan Cave <jonathan.cave@canonical.com>

5

6"""Test that Full Disk Encryption is in use.

7

8$ fde_tests.py

9 Canonical has a reference implementation of full disk encryption for IoT

10 devices. With no arguments passed this test checks this implementation is in

11 operation on the device under test.

12

13$ fde_tests.py desktop

14 Checks if the system appears to be using full disk encryption as configured by

15 the desktop installer.

16"""

17

18 import os

19 import subprocess as sp

20 import sys

21

22 from checkbox_support.snap_utils.system import get_series

23

24

25 def main():

26 on_desktop = len(sys.argv) > 1 and sys.argv[1] == "desktop"

27

28 # discover the underlying mount point for the encrypted part

29 if on_desktop:

30 cmd = "findmnt {} -n -o SOURCE".format("/")

31 else:

32 if int(get_series()) >= 20:

33 cmd = "findfs LABEL=ubuntu-data"

34 else:

35 cmd = "findfs LABEL=writable"

36 print("+ {}".format(cmd))

37 try:

38 source = (

39 sp.check_output(cmd, shell=True)

40 .decode(sys.stdout.encoding)

41 .strip()

42 )

43 except sp.CalledProcessError:

44 raise SystemExit(

45 "ERROR: could not find mountpoint for the encrypted partition"

46 )

47 print(source, "\n")

48

49 # resolve the source to an actual device node

50 print("+ realpath {}".format(source))

51 device = os.path.realpath(source)

52 print(device, "\n")

53

54 # work upwards through the tree of devices until we find the one that has

55 # the type 'crypt'

56 kname = os.path.basename(device)

57 while True:

58 cmd = 'lsblk -r -n -i -o KNAME,TYPE,PKNAME | grep "^{}"'.format(kname)

59 print("+ {}".format(cmd))

60 try:

61 lsblk = (

62 sp.check_output(cmd, shell=True)

63 .decode(sys.stdout.encoding)

64 .strip()

65 )

66 except sp.CalledProcessError:

67 raise SystemExit("ERROR: lsblk call failed")

68 _, devtype, parent = lsblk.split(maxsplit=2)

69 print(devtype, "\n")

70 if devtype == "crypt":

71 # found the device

72 break

73 if devtype == "disk":

74 # reached the physical device, end the search

75 raise SystemExit(

76 'ERROR: could not find a block device of type "crypt"'

77 )

78 kname = parent

79

80 # the presence of device with type 'crypt' is probably confirmation enough

81 # but to be really sure lets check to see it is found by cryptsetup

82

83 # first we need to know its mapper name

84 cmd = 'dmsetup info /dev/{} | grep "^Name:"'.format(kname)

85 print("+ {}".format(cmd))

86 try:

87 mapper_name = (

88 sp.check_output(cmd, shell=True)

89 .decode(sys.stdout.encoding)

90 .strip()

91 .split()[-1]

92 )

93 except sp.CalledProcessError:

94 raise SystemExit(

95 "ERROR: dmsetup info on device {} failed".format(kname)

96 )

97 print(mapper_name, "\n")

98

99 # then query the info in cryptsetup

100 cmd = "cryptsetup status {}".format(mapper_name)

101 print("+ {}".format(cmd))

102 try:

103 cryptinfo = (

104 sp.check_output(cmd, shell=True)

105 .decode(sys.stdout.encoding)

106 .strip()

107 )

108 except sp.CalledProcessError:

109 raise SystemExit("ERROR: cryptsetup check failed")

110 print(cryptinfo)

111 print("\nFull Disk Encryption is operational on this device")

112

113

114 if __name__ == "__main__":

115 main()