Manual test suite: use more precise data when comparing image size.
[tails.git] / features / step_definitions / veracrypt.rb
blob25b673dfde60cf66fdfe7fd4ebcc659680234dc7
1 # coding: utf-8
2 require 'expect'
3 require 'pty'
4 require 'tempfile'
6 $veracrypt_passphrase = 'asdf'
7 $veracrypt_hidden_passphrase = 'fdsa'
8 $veracrypt_volume_name = 'veracrypt'
10 def veracrypt_volume_size_in_GNOME(is_hidden)
11   is_hidden ? '52 MB' : '105 MB'
12 end
14 def create_veracrypt_keyfile()
15   keyfile = Tempfile.new('veracrypt-keyfile', $config["TMPDIR"])
16   keyfile << 'asdf'
17   keyfile.close
18   return keyfile.path
19 end
21 def reply_prompt(r_f, w_f, prompt_re, answer)
22   r_f.expect(prompt_re) do
23     debug_log "got prompt, typing #{answer}"
24     sleep 1 # tcplay takes some time before it's ready to read our input
25     w_f.puts "#{answer}"
26   end
27 end
29 def create_veracrypt_volume(type, with_keyfile)
30   @veracrypt_is_hidden = (type == 'hidden')
31   @veracrypt_needs_keyfile = with_keyfile
32   step "I temporarily create a 100 MiB raw disk named \"#{$veracrypt_volume_name}\""
33   disk_path = $vm.storage.disk_path($veracrypt_volume_name)
34   keyfile = create_veracrypt_keyfile()
35   fatal_system "losetup -f '#{disk_path}'"
36   loop_dev = `losetup -j '#{disk_path}'`.split(':').first
37   tcplay_create_cmd = "tcplay --create --device='#{loop_dev}'" \
38                     + " --weak-keys --insecure-erase"
39   tcplay_create_cmd += " --hidden" if @veracrypt_is_hidden
40   tcplay_create_cmd += " --keyfile='#{keyfile}'" if @veracrypt_needs_keyfile
41   debug_log "tcplay create command: #{tcplay_create_cmd}"
42   PTY.spawn(tcplay_create_cmd) do |r_f, w_f, pid|
43     begin
44       w_f.sync = true
45       reply_prompt(r_f, w_f, /^Passphrase:\s/, $veracrypt_passphrase)
46       reply_prompt(r_f, w_f, /^Repeat passphrase:\s/, $veracrypt_passphrase)
47       if @veracrypt_is_hidden
48         reply_prompt(r_f, w_f, /^Passphrase for hidden volume:\s/,
49                      $veracrypt_hidden_passphrase)
50         reply_prompt(r_f, w_f, /^Repeat passphrase:\s/,
51                      $veracrypt_hidden_passphrase)
52         reply_prompt(r_f, w_f, /^Size of hidden volume.*:\s/, '50M')
53       end
54       reply_prompt(r_f, w_f, /^\s*Are you sure you want to proceed/, 'y')
55       r_f.expect(/^All done!/)
56     rescue Errno::EIO
57     ensure
58       Process.wait pid
59     end
60     $?.exitstatus == 0 or raise "#{tcplay_create_cmd} exited with #{$?.exitstatus}"
61   end
62   tcplay_map_cmd = "tcplay --map=veracrypt --device='#{loop_dev}'"
63   tcplay_map_cmd += " --keyfile='#{keyfile}'" if @veracrypt_needs_keyfile
64   debug_log "tcplay map command: #{tcplay_map_cmd}"
65   PTY.spawn(tcplay_map_cmd) do |r_f, w_f, pid|
66     begin
67       w_f.sync = true
68       reply_prompt(r_f, w_f, /^Passphrase:\s/,
69                    @veracrypt_is_hidden ? $veracrypt_hidden_passphrase : $veracrypt_passphrase)
70       r_f.expect(/^All ok!/)
71     rescue Errno::EIO
72     ensure
73       Process.wait pid
74     end
75     $?.exitstatus == 0 or raise "#{tcplay_map_cmd} exited with #{$?.exitstatus}"
76   end
77   fatal_system "mkfs.vfat '/dev/mapper/veracrypt' >/dev/null"
78   Dir.mktmpdir('veracrypt-mountpoint', $config["TMPDIR"]) { |mountpoint|
79     fatal_system "mount -t vfat '/dev/mapper/veracrypt' '#{mountpoint}'"
80     # must match SecretFileOnVeraCryptVolume.png when displayed in GNOME Files
81     FileUtils.cp('/usr/share/common-licenses/GPL-3', "#{mountpoint}/SecretFile")
82     fatal_system "umount '#{mountpoint}'"
83   }
84   fatal_system "tcplay --unmap=veracrypt"
85   fatal_system "losetup -d '#{loop_dev}'"
86   File.delete(keyfile)
87 end
89 When /^I plug a USB drive containing a (.+) VeraCrypt volume( with a keyfile)?$/ do |type, with_keyfile|
90   create_veracrypt_volume(type, with_keyfile)
91   step "I plug USB drive \"#{$veracrypt_volume_name}\""
92 end
94 When /^I plug and mount a USB drive containing a (.+) VeraCrypt file container( with a keyfile)?$/ do |type, with_keyfile|
95   create_veracrypt_volume(type, with_keyfile)
96   @veracrypt_shared_dir_in_guest = share_host_files($vm.storage.disk_path($veracrypt_volume_name))
97   $vm.execute_successfully(
98     "chown #{LIVE_USER}:#{LIVE_USER} '#{@veracrypt_shared_dir_in_guest}/#{$veracrypt_volume_name}'"
99   )
102 When /^I unlock and mount this VeraCrypt (volume|file container) with Unlock VeraCrypt Volumes$/ do |support|
103   step 'I start "Unlock VeraCrypt Volumes" via GNOME Activities Overview'
104   case support
105   when 'volume'
106     @screen.wait_and_click('Gtk3UnlockButton.png', 10)
107   when 'file container'
108     @screen.wait_and_click('UnlockVeraCryptVolumesAddButton.png', 10)
109     @screen.wait('Gtk3FileChooserDesktopButton.png', 10)
110     @screen.type(@veracrypt_shared_dir_in_guest + '/' + $veracrypt_volume_name + Sikuli::Key.ENTER)
111   end
112   @screen.wait('VeraCryptUnlockDialog.png', 10)
113   @screen.type(
114     @veracrypt_is_hidden ? $veracrypt_hidden_passphrase : $veracrypt_passphrase
115   )
116   @screen.click('VeraCryptUnlockDialogHiddenVolumeLabel.png') if @veracrypt_is_hidden
117   @screen.type(Sikuli::Key.ENTER)
118   @screen.waitVanish('VeraCryptUnlockDialog.png', 10)
119   try_for(30) do
120       $vm.execute_successfully('ls /media/amnesia/*/SecretFile')
121   end
124 When /^I unlock and mount this VeraCrypt (volume|file container) with GNOME Disks$/ do |support|
125   step 'I start "Disks" via GNOME Activities Overview'
126   disks = Dogtail::Application.new('gnome-disks')
127   case support
128   when 'volume'
129     disks.children(roleName: 'table cell').find { |row|
130       /^105 MB Drive/.match(row.name)
131     }.grabFocus
132   when 'file container'
133     gnome_shell = Dogtail::Application.new('gnome-shell')
134     menu = gnome_shell.menu('Disks')
135     menu.click()
136     @screen.wait_and_click('GnomeDisksAttachDiskImageMenuEntry.png', 10)
137     # Once we use a more recent Dogtail that can deal with UTF-8 (#12185),
138     # we can instead do:
139     #   gnome_shell.child('Attach Disk Imageā€¦', roleName: 'label').click
140     # Otherwise Disks is sometimes minimized, for some reason I don't understand
141     sleep 2
142     attach_dialog = disks.child('Select Disk Image to Attach', roleName: 'file chooser', showingOnly: true)
143     attach_dialog.child('Set up read-only loop device', roleName: 'check box').click
144     filter = attach_dialog.child('Disk Images (*.img, *.iso)', roleName: 'combo box')
145     filter.click
146     try_for(5) do
147       begin
148         filter.child('All Files', roleName: 'menu item').click
149         true
150       rescue RuntimeError
151         # we probably clicked too early, which triggered an "Attempting
152         # to generate a mouse event at negative coordinates" Dogtail error
153         false
154       end
155     end
156     @screen.type(@veracrypt_shared_dir_in_guest + '/' + $veracrypt_volume_name)
157     sleep 2 # avoid ENTER being eaten by the auto-completion system
158     @screen.type(Sikuli::Key.ENTER)
159     try_for(15) do
160       begin
161         disks.children(roleName: 'table cell').find { |row|
162           /^105 MB Loop Device/.match(row.name)
163         }.grabFocus
164         true
165       rescue NoMethodError
166         false
167       end
168     end
169   end
170   disks.child('', roleName: 'panel', description: 'Unlock selected encrypted partition').click
171   unlock_dialog = disks.dialog('Set options to unlock')
172   passphrase_field = unlock_dialog.child('', roleName: 'password text')
173   passphrase_field.grabFocus()
174   passphrase_field.typeText(
175     @veracrypt_is_hidden ? $veracrypt_hidden_passphrase : $veracrypt_passphrase
176   )
177   if @veracrypt_needs_keyfile
178     # not accessible and unreachable with the keyboard (#15952)
179     @screen.click('GnomeDisksUnlockDialogKeyfileComboBox.png')
180     @screen.wait('Gtk3FileChooserDesktopButton.png', 10)
181     $vm.file_overwrite('/tmp/keyfile', 'asdf')
182     @screen.type('/tmp/keyfile' + Sikuli::Key.ENTER)
183     @screen.waitVanish('Gtk3FileChooserDesktopButton.png', 10)
184   end
185   @screen.wait_and_click('GnomeDisksUnlockDialogHiddenVolumeLabel.png', 10) if @veracrypt_is_hidden
186   # Clicking is robust neither with Dogtail (no visible effect) nor with Sikuli
187   # (that sometimes clicks just a little bit outside of the button)
188   @screen.wait('Gtk3UnlockButton.png', 10)
189   @screen.type('u', Sikuli::KeyModifier.ALT) # "Unlock" button
190   try_for(10, :msg => "Failed to mount the unlocked volume") do
191     begin
192       unlocked_volume = disks.child('105 MB VeraCrypt/TrueCrypt', roleName: 'panel', showingOnly: true)
193       unlocked_volume.click
194       # Move the focus down to the "Filesystem\n107 MB FAT" item (that Dogtail
195       # is not able to find) using the 'Down' arrow, in order to display
196       # the "Mount selected partition" button.
197       unlocked_volume.grabFocus()
198       sleep 0.5 # otherwise the following key press is sometimes lost
199       disks.pressKey('Down')
200       disks.child('', roleName: 'panel', description: 'Mount selected partition', showingOnly: true).click
201       true
202     rescue RuntimeError
203       # we probably did something too early, which triggered a Dogtail error
204       # such as "Attempting to generate a mouse event at negative coordinates"
205       false
206     end
207   end
208   try_for(10, :msg => "/media/amnesia/*/SecretFile does not exist") do
209     $vm.execute_successfully('ls /media/amnesia/*/SecretFile')
210   end
213 When /^I open this VeraCrypt volume in GNOME Files$/ do
214   $vm.spawn('nautilus /media/amnesia/*', user: LIVE_USER)
215   Dogtail::Application.new('nautilus').window(
216     veracrypt_volume_size_in_GNOME(@veracrypt_is_hidden) + ' Volume'
217   )
220 When /^I lock the currently opened VeraCrypt (volume|file container)$/ do |support|
221   $vm.execute_successfully(
222     'udisksctl unmount --block-device /dev/mapper/tcrypt-*',
223     :user => LIVE_USER
224   )
225   device = support == 'volume' ? '/dev/sda' : '/dev/loop1'
226   $vm.execute_successfully(
227     "udisksctl lock --block-device #{device}",
228     :user => LIVE_USER
229   )
232 Then /^the VeraCrypt (volume|file container) has been unmounted and locked$/ do |support|
233   assert(! $vm.execute('ls /media/amnesia/*/SecretFile').success?)
234   assert(! $vm.execute('ls /dev/mapper/tcrypt-*').success?)