--- /dev/null
+# 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