Add tests for timestamp ratchet
authorJacob Bachmeyer <jcb@gnu.org>
Fri, 21 Oct 2022 01:48:56 +0000 (20:48 -0500)
committerJacob Bachmeyer <jcb@gnu.org>
Fri, 21 Oct 2022 01:48:56 +0000 (20:48 -0500)
testsuite/gatekeeper.all/11_ratchet.exp [new file with mode: 0644]
testsuite/lib/gatekeeper.exp

diff --git a/testsuite/gatekeeper.all/11_ratchet.exp b/testsuite/gatekeeper.all/11_ratchet.exp
new file mode 100644 (file)
index 0000000..acc3b2c
--- /dev/null
@@ -0,0 +1,322 @@
+# Tests for timestamp ratchet
+
+# Copyright (C) 2022 Jacob Bachmeyer
+#
+# This file is part of a testsuite for the GNU FTP upload system.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+proc check_ratchet_handling { desc args } {
+    set tenv [new_test_environment]
+
+    array set data { setup {} rounds {} }
+    array set data $args
+
+    array set setup {
+       files {} symlinks {} packages {}
+    }
+    array set setup $data(setup)
+
+    register_test_packages $tenv $setup(packages)
+
+    foreach {zone filelist} $setup(files) {
+       foreach {file sig} $filelist {
+           file mkdir [file dirname [file join $tenv $zone $file]]
+           put_file [file join $tenv $zone $file] "${file}\n"
+           age_file [file join $tenv $zone $file] "10 minutes ago"
+           if { [llength $sig] > 0 } {
+               put_file [file join $tenv $zone "${file}.sig"] \
+                   [sign_test_file [file tail $file] \
+                        [eval [list make_test_signature] $sig]]
+               age_file [file join $tenv $zone "${file}.sig"] "10 minutes ago"
+           }
+       }
+    }
+    foreach {zone linklist} $setup(symlinks) {
+       foreach {target link} $linklist {
+           # The Tcl [file link] command refuses to create dangling symlinks.
+           # file link -symbolic [file join $tenv $zone $link] $target
+           verbose -log [exec ln -sv $target [file join $tenv $zone $link]]
+       }
+    }
+
+    foreach {round} $data(rounds) {
+       array set step { upload {} check {} }
+       array set step $round
+
+       array set check {
+           email-to {} log {} file-tree {}
+       }
+       array set check $step(check)
+
+       set log_items {
+           start "ftp-upload start message"
+           mail,addresses "outgoing mail sent"
+       }
+       if { [llength $check(log)] > 0 } { append log_items $check(log) }
+
+       foreach {tag case} $step(upload) {
+           make_test_case $tenv [list $tag $case]
+       }
+
+       start_test_services $tenv
+       run_upload_batch_test
+       stop_test_services
+
+       analyze_log $tenv "ratchet: $desc" $log_items
+       analyze_mail $tenv "ratchet: $desc" to $check(email-to)
+       if { [llength $check(file-tree)] > 0 } {
+           foreach {zones mode items} $check(file-tree) {
+               analyze_file_tree $tenv "ratchet: $desc" \
+                   $zones $mode $items
+           }
+       }
+    }
+
+    close_test_environment $tenv
+}
+
+# ----------------------------------------
+
+check_ratchet_handling "simple replay of new upload" setup {
+    packages {
+       foo {
+           email { "foo@example.org" }
+           keys { { id 1000 name "foo <foo@example.gnu.org>" } }
+           maintainers { "foo <foo@example.net>" }
+       }
+    }
+} rounds {
+    { upload {
+       foo.bin {
+           directive {
+               version 1.2
+               filename foo.bin
+               directory foo
+           } dsig { good 00 1000 "Oct 20 18:50:42 CDT 2022" }
+           file { test } fsig { good 01 1000 "Oct 20 18:50:42 CDT 2022" }
+       }
+    } check {
+       file-tree {
+           { incoming in-stage stage archive } empty {}
+           { pub } files {
+               foo/foo.bin.sig foo/foo.bin
+           }
+       } log {
+           found,foo.bin.directive.asc "found directive in triplet"
+           found-packet,foo.bin.directive.asc:foo.bin.sig:foo.bin \
+               "found triplet"
+           workdone,1 "1 upload processed"
+       } email-to {
+           ftp-upload-script@gnu.org foo@example.gnu.org
+           ftp-upload-report@gnu.org foo@example.org foo@example.net
+       }
+    } }
+    { upload {
+       foo.bin {
+           directive {
+               version 1.2
+               replace true
+               directory foo
+               filename foo.bin
+           } dsig { good 02 1000 "Oct 20 18:50:42 CDT 2022" }
+           file { test } fsig { good 03 1000 "Oct 20 18:50:42 CDT 2022" }
+       }
+    } check {
+       file-tree {
+           { incoming stage archive } empty {}
+           { in-stage } files {
+               foo.bin.directive.asc foo.bin.sig foo.bin
+           }
+           { pub } files {
+               foo/foo.bin.sig foo/foo.bin
+           }
+       } log {
+           found,foo.bin.directive.asc "found directive in triplet"
+           found-packet,foo.bin.directive.asc:foo.bin.sig:foo.bin \
+               "found triplet"
+           validate,older-signature-timestamp "simple replay detected"
+       } email-to {
+           ftp-upload-script@gnu.org foo@example.gnu.org
+           ftp-upload-report@gnu.org foo@example.org foo@example.net
+       }
+    } }
+}
+
+check_ratchet_handling "replace new upload" setup {
+    packages {
+       foo {
+           email { "foo@example.org" }
+           keys { { id 1000 name "foo <foo@example.gnu.org>" } }
+           maintainers { "foo <foo@example.net>" }
+       }
+    }
+} rounds {
+    { upload {
+       foo.bin {
+           directive {
+               version 1.2
+               filename foo.bin
+               directory foo
+           } dsig { good 00 1000 "Oct 20 18:50:42 CDT 2022" }
+           file { test } fsig { good 01 1000 "Oct 20 18:50:42 CDT 2022" }
+       }
+    } check {
+       file-tree {
+           { incoming in-stage stage archive } empty {}
+           { pub } files {
+               foo/foo.bin.sig foo/foo.bin
+           }
+       } log {
+           found,foo.bin.directive.asc "found directive in triplet"
+           found-packet,foo.bin.directive.asc:foo.bin.sig:foo.bin \
+               "found triplet"
+           workdone,1 "1 upload processed"
+       } email-to {
+           ftp-upload-script@gnu.org foo@example.gnu.org
+           ftp-upload-report@gnu.org foo@example.org foo@example.net
+       }
+    } }
+    { upload {
+       foo.bin {
+           directive {
+               version 1.2
+               replace true
+               directory foo
+               filename foo.bin
+           } dsig { good 02 1000 "Oct 20 18:52:42 CDT 2022" }
+           file { test } fsig { good 03 1000 "Oct 20 18:52:42 CDT 2022" }
+       }
+    } check {
+       file-tree {
+           { incoming in-stage stage } empty {}
+           { archive } archived-files {
+               foo/foo.bin
+           }
+           { pub } files {
+               foo/foo.bin.sig foo/foo.bin
+           }
+       } log {
+           found,foo.bin.directive.asc "found directive in triplet"
+           found-packet,foo.bin.directive.asc:foo.bin.sig:foo.bin \
+               "found triplet"
+           workdone,1 "1 upload processed"
+       } email-to {
+           ftp-upload-script@gnu.org foo@example.gnu.org
+           ftp-upload-report@gnu.org foo@example.org foo@example.net
+       }
+    } }
+}
+
+check_ratchet_handling "replay replaced upload" setup {
+    packages {
+       foo {
+           email { "foo@example.org" }
+           keys { { id 1000 name "foo <foo@example.gnu.org>" } }
+           maintainers { "foo <foo@example.net>" }
+       }
+    }
+} rounds {
+    { upload {
+       foo.bin {
+           directive {
+               version 1.2
+               replace true
+               directory foo
+               filename foo.bin
+           } dsig { good 00 1000 "Oct 20 18:50:42 CDT 2022" }
+           file { test } fsig { good 01 1000 "Oct 20 18:50:42 CDT 2022" }
+       }
+    } check {
+       file-tree {
+           { incoming in-stage stage archive } empty {}
+           { pub } files {
+               foo/foo.bin.sig foo/foo.bin
+           }
+       } log {
+           found,foo.bin.directive.asc "found directive in triplet"
+           found-packet,foo.bin.directive.asc:foo.bin.sig:foo.bin \
+               "found triplet"
+           workdone,1 "1 upload processed"
+       } email-to {
+           ftp-upload-script@gnu.org foo@example.gnu.org
+           ftp-upload-report@gnu.org foo@example.org foo@example.net
+       }
+    } }
+    { upload {
+       foo.bin {
+           directive {
+               version 1.2
+               replace true
+               directory foo
+               filename foo.bin
+           } dsig { good 02 1000 "Oct 20 18:52:42 CDT 2022" }
+           file { test } fsig { good 03 1000 "Oct 20 18:52:42 CDT 2022" }
+       }
+    } check {
+       file-tree {
+           { incoming in-stage stage } empty {}
+           { archive } archived-files {
+               foo/foo.bin
+           }
+           { pub } files {
+               foo/foo.bin.sig foo/foo.bin
+           }
+       } log {
+           found,foo.bin.directive.asc "found directive in triplet"
+           found-packet,foo.bin.directive.asc:foo.bin.sig:foo.bin \
+               "found triplet"
+           workdone,1 "1 upload processed"
+       } email-to {
+           ftp-upload-script@gnu.org foo@example.gnu.org
+           ftp-upload-report@gnu.org foo@example.org foo@example.net
+       }
+    } }
+    { upload {
+       foo.bin {
+           directive {
+               version 1.2
+               replace true
+               directory foo
+               filename foo.bin
+           } dsig { good 00 1000 "Oct 20 18:50:42 CDT 2022" }
+           file { test } fsig { good 01 1000 "Oct 20 18:50:42 CDT 2022" }
+       }
+    } check {
+       file-tree {
+           { incoming stage } empty {}
+           { in-stage } files {
+               foo.bin.directive.asc foo.bin.sig foo.bin
+           }
+           { archive } archived-files {
+               foo/foo.bin
+           }
+           { pub } files {
+               foo/foo.bin.sig foo/foo.bin
+           }
+       } log {
+           found,foo.bin.directive.asc "found directive in triplet"
+           found-packet,foo.bin.directive.asc:foo.bin.sig:foo.bin \
+               "found triplet"
+           validate,older-signature-timestamp "replay detected"
+       } email-to {
+           ftp-upload-script@gnu.org foo@example.gnu.org
+           ftp-upload-report@gnu.org foo@example.org foo@example.net
+       }
+    } }
+}
+
+# ----------------------------------------
+
+#EOF
index ff6d7c2ea095311d2874a161c0fed8ced771df1b..4bddcee8e094098adae2f37bd354bd393f43ceb8 100644 (file)
@@ -764,6 +764,12 @@ proc analyze_log { base_dir name assess } {
                     set A(validate,future-signature-timestamp) 1
                     exp_continue
                 }
+       -re {^gatekeeper\[[0-9]+\]: \(Test\)\
+                GPG signed upload older than/same timestamp[^\r\n]+} {
+                    # from read_directive_file, if signature timestamp bad
+                    set A(validate,older-signature-timestamp) 1
+                    exp_continue
+                }
 
        -re {^gatekeeper\[[0-9]+\]: \(Test\)\
                 DEBUG: [^ ]+ size is [[:digit:]]+} {