starting new job...
→ running command: cd "/etc/byggsteg/job-clone//bewaking/trunk" && git reset --hard && git pull
HEAD is now at 8bc1087 update: Test library
From https://codeberg.org/jjba23/bewaking
8bc1087..67774e2 trunk -> origin/trunk
* [new tag] v0.4.1 -> v0.4.1
Updating 8bc1087..67774e2
Fast-forward
.woodpecker/tag-bumper.yaml | 49 ---------------------------------------------
1 file changed, 49 deletions(-)
delete mode 100644 .woodpecker/tag-bumper.yaml
→ running command: cd /etc/byggsteg/job-clone/bewaking/trunk && uname -a
Linux wolk-jjba 7.1.5-gnu #1 SMP PREEMPT_DYNAMIC 1 x86_64 GNU/Linux
→ running command: cd /etc/byggsteg/job-clone/bewaking/trunk && git log --name-status HEAD^..HEAD
commit 67774e269936da12004c18b0fe00e41b625e07d1
Author: Josep Bigorra <jjbigorra@gmail.com>
Date: Wed Oct 14 14:35:55 2026 +0200
ci: Simplify CI workflows
D .woodpecker/tag-bumper.yaml
→ running command: cd /etc/byggsteg/job-clone/bewaking/trunk && maak test
scanning directories for unit tests...
scanning present working directory for unit tests...
shuffled the files with seed 844052010 (replay with VERITAS_SEED=844052010)
found unit tests: (bewaking-spec.scm crypto-spec.scm web-spec.scm cli-spec.scm password-spec.scm)
running unit tests from file: bewaking-spec.scm, entrypoint: spec
owner-details: ((id . "444959d3-7a8e-455e-b608-31e35145a2fa") (email . "someone@jointhefreeworld.org") (name . "Legolas son of Thandruil") (permissions byggsteg:modify-permissions byggsteg:job-create byggsteg:job-delete byggsteg:profile-create byggsteg:profile-delete byggsteg:user-create byggsteg:user-delete))
generate-token: BWK3.default.1791981711.-.01e7cf0a1a9524ac62873ff7e5b4eb9b315927618a8a2d24747ef7515ae182ee392deef444b3350ec6f6a02b2bd71bc2cf7b0bd4800a4b556827aa0bb91f362bd60ec42372d8f88e64f1a8697d7bd939d0067ae290df004a681f0eaedc95a6ddc953366a91e679f10a1c0f2bf5eb9bb4bdb928b497c036d634de2116510526a1211efe65210d9590edb731f08d287af66074c48e0e89340350980072cdcc1b2d517651b266bef0da65472d8f7263746212122eed70cf9cf81c559d076cfc81fd25009c41f67fc93cb4f63a90d4ed4d0430cd2babc75a325e604152bef2dc484171d33535b656626d1fe3a82240540be4c26831512076651e6c8414bada6f08e2ebba0deea8e3a80d194201a698a822a6b6871daacf9ef46eeebd041a0aa7d79cf192e25c43d8d5bd959bf2322dec1636abb54827fcc4d98313e90f345568286e298fe9f91dbee95586eeb817663aa5a8816880d43f4e1e1f085a7dcb1165797db6a22b95b71fac2cab6bd6b4bc0a0d2232dd1da8752731c7b28ac2325f070dfa7fe75e83b52ac825a6430b587067e7607c58226475c4c85312135cf5ae91767655feab3a80540fe1515b8dc62d80e3a59d0f09a0bad7b0e86e3efab051af010bdb0aee1ca4ad8b620045306f3dce2168703408cd8328bec05f19975540167f05b696057032591b3b47b0e6128dda3c0cbebdd3eeee38c97dc1b4af9945064cd5e0bf1c39774a4f799e53c72ee6f0b1c569afa67c97cbef92c866ef4c3428d53b0f8ee3b0b83b4b5d645c8cf3d91192614a69cbdf477cb2bed08c79e0218d8cf811a92bec03731480798d3b5930efcb8c2615bd4aac46b33360d2f99d9ef0a02b861a7048ac5a064a8dd2b91298ea774c77777f67ef2bfad5815ffcaff3fa1e070a3dd0fcc393a3ac9ff3807f00dab99ec7d482e7a30c13a87c0f19ee2371f4cda6b27dffd083e5194b1c875f55edb52b99f341aa0228dffa1af96b455eaf191ef9830bc144776f3b06c806a10dbabb7b5852e644efe34f51bb29aea826210757b3f4aad7c9b12146cadab5f67c83ee3d3aab37ec4c77dfb1a3e5936e7a9b1e135a559a58fbe1a7a5c6ea348c70915fe9d51812bb5538d0814e3e2a8bdd33993c89c568b9084e4f3fc71f678b66a1ea09814c9a1fccf298e54c6c663da4e2f7b0c5c56d745feee2d891c22d0165b933a65acbd385245cb0d5745bb09d87c9fc17c43844d8e5a8b688064f6e51767f808ca63399497869d68a0a6d156852e0288a3593d39c7c1d4666ea36b987014c76005ee36f81302e29229b
error decrypting token: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting token: misc-error: (#f ~A (open-envelope: not a bewaking envelope (expected BWK3).) #f)
owner-details: ((id . "444959d3-7a8e-455e-b608-31e35145a2fa") (email . "someone@jointhefreeworld.org") (name . "Legolas son of Thandruil") (permissions byggsteg:modify-permissions byggsteg:job-create byggsteg:job-delete byggsteg:profile-create byggsteg:profile-delete byggsteg:user-create byggsteg:user-delete))
generate-token: BWK3.default.1791981711.-.018481981e2f8785aec95b02b24d4c6a405ea4b14e7bb8cb199582eb4648e6056f249a33eb0f07c51f50d1ebc881b769e9449d4036827beed523642f97ea96d787702a6298343320baa3dbcea0afa8d2c3ff1b7bb50eb13fc306b253985110a4d62561f24685f594535c39ea2cb4ffbf03d97b0ea2d18a5d9ccf302a445ed65b87257843035befb26fa518fcfd65d7dc5e601401a73adc29e89c1c9c4bd09009265e26a16e8f7ebc87bdd3139f1eb3657e9604667996a9cc1b10a8d6fb4d250eaee8c861e6b61008c1b62cb5b9d7eeab2120d3aa2e1e5368d3975aaac200f1ba6d39bda85acd5d1d8bc0947a49180a201c1c94a6c23950ff0e6b2bb1f45ae73153f33af3d051027ffb5a9d5cbdecca32a98621c177f1a75d6d1c903c57ffa49069af862d1403301cda6c0aa30d44b7ece72cde79eaa108458db6050687af998b7ccb81ba57f34979ab05812612b1286272dd335c2ae23148ccff0334abc4098d99dcbe0d9175ba0c2c879ce705ba16be91a70bd8c365f95e15919bb9f1adb31e94973e52232738e659e344b04d35499b16768384a577473976870df44aa06e57d388b23562d309a68a460dd488ad008d57308c0fd104249d2e707515c384c5c50c22f19d12a8535f577c338a544a28321acc0f713e70080db6945a49fa158399a44db29bfd9ad98889f98a3458d7e988f861172049e168cb70167ea187619bcec41f42f127a080ac2becc50a707a4725de408caf54947f1fb9bab25af550b0b984ce317c0cc02f98644b24f8299eee685b89e3cdf9b945af35ee3e7d996e32e1e1b1693b7d80e8f99d3d17ef458daca232a13caa4277b6d18a63dafa1db72a8aea4e17516358b676d579867664d3e461548e5c06aa16efeae9c836093157595a6b1b3a329d4f48d59fb473ebffea74db2ca9d4a01aceff36e61dc07d15c3a273e72eca31cfe3728091b2167c3fe3bf1e83d4a8989a3564edc9308af94684be721a08a5f6e1b4523df8de31544e1fa4c4e02020e49dfd15aa63f78baebf60b3c60ef2f1ec62b64f3de397c93fa2d6ec096580cbfc275501989d89b78c98dcf41c40799defa72e40a5f822407deae133b29a5167377fadb370b79f48d45d287a44f104723ec5cdfc5a2ceeaff4f2bb5dc1df23ce311d475d3acd4c67a92e2613bf368d1d9401f16e093c1d21ed18a68cf1724ac059a4f235fbbf7dbeefb297268571a7bc843301fd10beb85ea2fd6bf6a168a8f881ac156737e83aff60a2e0d736aa8399c9cba678bf7b6ddbad98f4074373729554c942be7942
decoded: ((token-type . bewaking-token) (token-id . 3ff46a92-f947-41c4-b60b-3410f02874c1) (token-created-at . 1791981711) (token-expires-at . 1792586511) (owner-id . 444959d3-7a8e-455e-b608-31e35145a2fa) (owner-email . someone@jointhefreeworld.org) (owner-name . Legolas son of Thandruil) (owner-permissions byggsteg:modify-permissions byggsteg:job-create byggsteg:job-delete byggsteg:profile-create byggsteg:profile-delete byggsteg:user-create byggsteg:user-delete))
error decrypting object: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting token: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting object: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting token: misc-error: (#f ~A (open-envelope: not a bewaking envelope (expected BWK3).) #f)error decrypting object: misc-error: (#f ~A (open-envelope: not a bewaking envelope (expected BWK3).) #f)error decrypting token: misc-error: (#f ~A (open-envelope: not a bewaking envelope (expected BWK3).) #f)error decrypting object: misc-error: (#f ~A (open-envelope: not a bewaking envelope (expected BWK3).) #f)error decrypting object: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting object: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting token: misc-error: (#f ~A (open-envelope: the sealed data expired at 1000 (key 'default').) #f)error decrypting object: misc-error: (#f ~A (open-envelope: the sealed data expired at 1000 (key 'default').) #f)
?? info - shuffled with seed 844052010 (replay with VERITAS_SEED=844052010)
+----------------------------------------------+
[? suite] Garbling and Ungarbling
[? test] garble-ungarble
? got: ow
? got: dlrow
? got: dlrow olleh
? got: hello world
? got: w
? got: np3ld1h3o9p2f18plzqjl8ghyi7ak9wdald8j6xcbd4y33pwbml62p837y750qm6incwwjubxwm4fl7tprvdr0kwwto1k6lsnhxr529rbga47em1aq7evwknolg6r5gp
? got: lrow
? got: row
+----------------------------------------------+
[? suite] Permissions
[? test] has-needed-permissions?
? got: #t
? got: #t
? got: #f
? got: #f
? got: #f
? got: #f
? got: #t
? got: #t
+----------------------------------------------+
[? suite] Lossless Token Decryption
[? test] lossless-token
[?? assert] works properly with correct details
? got: #t
+----------------------------------------------+
[? suite] Generic object encryption
[? test] encrypt-object and decrypt-object
[?? assert] round-trips a plain string payload
? got: just a string
[?? assert] round-trips a list payload
? got: (1 2 3)
[?? assert] fails gracefully with a wrong signing key
? got: #f
[?? assert] round-trips a number payload
? got: 1234
[? test] decrypted data is data, never code
[?? assert] Guile refuses the #. read-eval syntax
? got: #t
[?? assert] a sealed payload that looks like code stays a string
? got: #.(+ 1 2)
+----------------------------------------------+
[? suite] generate-token
[? test] works properly with correct details
? got: #t
+----------------------------------------------+
[? suite] Token expiry
[? test] is-token-expired?
[?? assert] a malformed token-expires-at raises an error
? success catching error: misc-error: (#f is-token-expired?: Token is invalid. Missing or malformed 'token-expires-at' () #f)
[?? assert] a token whose expiry is in the future is not expired
? got: #f
[?? assert] a far-future token is not expired
? got: #f
[?? assert] a missing token-expires-at raises an error
? success catching error: misc-error: (#f is-token-expired?: Token is invalid. Missing or malformed 'token-expires-at' () #f)
[?? assert] a token whose expiry is in the past is expired
? got: #t
+----------------------------------------------+
[? suite] Sealed envelope (versioned, authenticated)
[? test] key rotation with a keyring
[?? assert] a keyring seals and opens by key id
? got: #t
[? test] knowing the format is not knowing the plaintext
[?? assert] the format is not hidden, only the content is
? got: #t
[?? assert] the plaintext, its Base16 and its Base64 do not appear in the envelope
? got: #t
[?? assert] the public metadata is exactly what is authenticated
? got: (version key-id created-at expires-at)
[? test] data that is not a BWK3 envelope is refused
[?? assert] a bare Base16 object is refused
? got: #f
[?? assert] recognition is a version-tag check, not a structure check
? got: #t
[?? assert] an unsupported version raises rather than being guessed at
? got: #t
[?? assert] a pre-versioning bare Base16 token is refused
? got: #f
[?? assert] a truncated envelope is refused when it is opened
? got: #t
[?? assert] an unknown envelope version is not recognised
? got: #f
[?? assert] a BWK3 envelope carrying an extra MAC field is refused
? got: #t
[?? assert] arbitrary text is not an envelope
? got: #f
[? test] seal-envelope and open-envelope
[?? assert] a wrong signing key fails the cipher check
? got: #t
[?? assert] the module exposes the current format version
? got: BWK3
[?? assert] the plaintext round-trips
? got: top secret payload
[?? assert] passing a MAC key to open-envelope is refused, not ignored
? got: #t
[?? assert] the result is a versioned envelope
? got: #t
[?? assert] BWK3 is the only version produced and read
? got: (BWK3)
[?? assert] the envelope starts with the version tag
? got: #t
[?? assert] a tampered envelope fails the integrity check
? got: #t
[? test] editing a genuine envelope never yields a plaintext
[?? assert] and every mutation of a real sealed object too
? got: #t
[?? assert] every single-digit mutation of a real envelope is refused
? got: #t
[? test] a refusal never says which key id the keyring configures
[?? assert] and it does not name a configured key id either
? got: #f
[?? assert] an expired genuine envelope is still told apart, since it authenticated first
? got: #f
[?? assert] an unknown key id and a tampered body fail identically
? got: (misc-error #f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)
[?? assert] a keyring with no matching entry fails identically
? got: (misc-error #f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)
[?? assert] a key that is not active yet fails identically
? got: (misc-error #f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)
[?? assert] the refusal is a refusal, not an accidental success
? got: #t
[?? assert] the refusal does not name the key id it could not resolve
? got: #f
[?? assert] a retired key fails identically
? got: (misc-error #f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)
[?? assert] nor does it carry any key material
? got: #f
[? test] the requested key id is embedded in generated tokens
[?? assert] the token carries the key id
? got: #t
[? test] the same plaintext never seals to the same value
[?? assert] a fresh salt and nonce make each envelope unique
? got: #t
[?? assert] equal objects seal to different strings
? got: #f
[? test] tokens and objects are envelopes and reject tampering
[?? assert] a tampered object is rejected
? got: #t
[?? assert] a tampered token is rejected
? got: #t
[?? assert] generated tokens are enveloped
? got: #t
[?? assert] sealed objects are enveloped
? got: #t
[? test] no reader is left for the earlier formats
[?? assert] no metadata is read out of a BWK2 envelope
? got: #f
[?? assert] no metadata is read out of a BWK1 envelope
? got: #f
[?? assert] a BWK1 object is refused likewise
? got: #f
[?? assert] a BWK2 envelope is not recognised
? got: #f
[?? assert] a BWK1 envelope is not recognised
? got: #f
[?? assert] a BWK2 envelope with no expiry is not recognised
? got: #f
[?? assert] an unreadable envelope is never reported as live
? got: #f
[?? assert] a BWK2 token is refused even with the keys that sealed it
? got: #f
+----------------------------------------------+
[? suite] Decryption
[? test] decrypt-token
[?? assert] fails gracefully when signing key is not valid
? got: #f
[?? assert] works properly with valid token
? got: #t
[?? assert] fails gracefully when token is not valid
? got: #f
+----------------------------------------------+
[? suite] Token validity helpers
[? test] token-remaining-seconds and token-valid?
[?? assert] remaining seconds is positive for a live token
? got: #t
[?? assert] a missing expiry raises an error
? success catching error: misc-error: (#f token-remaining-seconds: Token is invalid. Missing or malformed 'token-expires-at' () #f)
[?? assert] a freshly generated token is not expired
? got: #f
[?? assert] remaining seconds is negative for an expired token
? got: #t
+----------------------------------------------+
[? suite] Envelope metadata (key id, created-at, expiry)
[? test] keyring validity windows enforce rotation
[?? assert] a key inside its window seals and opens
? got: #t
[?? assert] a key past not-after cannot open data
? got: #t
[?? assert] no keyring is needed to read metadata
? got: #t
[?? assert] a key before not-before cannot seal data
? got: #t
[? test] a malformed envelope is rejected
[?? assert] a body that is not hexadecimal is refused
? got: #t
[?? assert] a non-numeric created-at is refused
? got: #t
[?? assert] a missing body is refused
? got: #t
[?? assert] a truncated body is refused
? got: #t
[?? assert] an empty key id is refused
? got: #t
[?? assert] sealing a non-string plaintext is refused
? got: #t
[?? assert] sealing without a signing key is refused
? got: #t
[? test] ttl-seconds records an absolute expiry
[?? assert] the envelope is live before its expiry
? got: #f
[?? assert] expires-at is created-at plus the ttl
? got: 4600
[?? assert] created-at is the requested timestamp
? got: 1000
[?? assert] the envelope is expired after its expiry
? got: #t
[? test] expiry is enforced when opening
[?? assert] max-age-seconds enforces a rotation policy
? got: #t
[?? assert] an expired envelope is refused
? got: #t
[?? assert] enforce-expiry? #f still opens it
? got: ttl payload
[? test] metadata is reported without any key
[?? assert] metadata is only read from envelopes
? got: #f
[?? assert] created-at is a Unix timestamp
? got: #t
[?? assert] the format version is reported
? got: BWK3
[?? assert] a malformed BWK3 envelope reports no metadata
? got: #f
[?? assert] there is no expiry unless one was requested
? got: #f
[?? assert] the key id is reported
? got: default
[?? assert] an envelope of a dropped format reports no metadata
? got: #f
[? test] expired tokens and objects are refused
[?? assert] a token sealed with a past expiry is refused
? got: #t
[?? assert] expiry auditing ignores non-envelopes
? got: #f
[?? assert] an object sealed with a past expiry is refused
? got: #t
[? test] the metadata is authenticated
[?? assert] relabelling the version is refused
? got: #t
[?? assert] editing created-at in a BWK3 envelope breaks its tag
? got: #t
[?? assert] extending the expiry in the envelope breaks the tag
? got: #t
[?? assert] editing the body breaks the tag
? got: #t
[?? assert] editing the key id breaks the tag
? got: #t
[?? assert] removing the expiry marker breaks the tag
? got: #t
[?? assert] backdating created-at breaks the tag
? got: #t
[? test] invalid sealing options are rejected
[?? assert] expires-at and ttl-seconds are mutually exclusive
? got: #t
[?? assert] a key id containing a dot is rejected
? got: #t
[?? assert] a non-integer ttl-seconds is rejected
? got: #t
?? info - ran 10 tasks in: 0 seconds, 22908 microseconds
running unit tests from file: crypto-spec.scm, entrypoint: spec
?? info - shuffled with seed 844052010 (replay with VERITAS_SEED=844052010)
+----------------------------------------------+
[? suite] Argon2id key derivation (RFC 9106)
[? test] the RFC 9106 Argon2id vector is reproduced
[?? assert] password 32x01, salt 16x02, secret 8x03, data 12x04, m=32, t=3, p=4
? got: 0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659
[? test] every parameter changes the derived bytes
[?? assert] a different salt gives a different tag
? got: #f
[?? assert] a different passphrase gives a different tag
? got: #f
[?? assert] a different pass count gives a different tag
? got: #f
[? test] invalid parameters are rejected
[?? assert] zero lanes is refused
? got: #t
[?? assert] a non-integer memory cost is refused
? got: #t
[?? assert] zero passes is refused
? got: #t
[?? assert] this libgcrypt provides Argon2
? got: #t
[?? assert] less than 8 KiB per lane is refused
? got: #t
[?? assert] a non-bytevector secret is refused
? got: #t
+----------------------------------------------+
[? suite] Randomness
[? test] random-bytes returns genuinely fresh bytes
[?? assert] the default length is the OpenSSL salt length
? got: 8
[?? assert] the requested length is honoured
? got: 32
[?? assert] two calls do not return the same bytes
? got: #t
[?? assert] a draw is not a constant
? got: #f
[?? assert] a draw is not all zeroes
? got: #t
+----------------------------------------------+
[? suite] Base16 encoding
[? test] malformed input is rejected
[?? assert] embedded whitespace raises
? success catching error: misc-error: (#f bewaking: malformed hexadecimal input (odd number of digits). () #f)
[?? assert] non-hexadecimal characters raise
? success catching error: misc-error: (#f bewaking: malformed hexadecimal input. () #f)
[?? assert] an odd number of digits raises
? success catching error: misc-error: (#f bewaking: malformed hexadecimal input (odd number of digits). () #f)
[? test] known encodings
[?? assert] the empty string encodes to nothing
? got:
[?? assert] a word encodes byte per byte
? got: 626577616b696e67
[?? assert] lowercase hex digits are produced
? got: 68656c6c6f
[? test] decoding accepts both cases
[?? assert] encoding then decoding is lossless
? got: hello
[?? assert] uppercase hex decodes too
? got: hello
[?? assert] lowercase hex decodes
? got: hello
+----------------------------------------------+
[? suite] PBKDF2-HMAC-SHA256
[? test] the key and IV material OpenSSL derives
[?? assert] 48 bytes over 10000 iterations, as `openssl kdf' reports
? got: cb14e1e1f7ac2fe57f2fcaa462d4ad903867450d9778d2ccf939d8ad3cb99982e0edbd06820c0a636f6fd53d4be339fd
[? test] published password/salt vectors
[?? assert] one iteration
? got: 120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b
[?? assert] 4096 iterations
? got: c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a
[?? assert] two iterations
? got: ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43
+----------------------------------------------+
[? suite] Key-derivation specifications
[? test] unsupported specifications are refused
[?? assert] an unknown KDF name is refused
? got: #t
[?? assert] an out-of-range Argon2id memory cost is refused
? got: #t
[?? assert] a PBKDF2 cost the OpenSSL format cannot record is refused
? got: #t
[?? assert] a string is not a KDF specification
? got: #t
[? test] the default is PBKDF2 with its fixed cost
[?? assert] the module default names PBKDF2
? got: pbkdf2
[?? assert] its iteration count is the OpenSSL one
? got: 10000
[? test] Argon2id defaults are filled in and reported
[?? assert] the defaults are 64 MiB, 3 passes, 1 lane
? got: ((kdf . argon2id) (memory-kib . 65536) (iterations . 3) (parallelism . 1))
[?? assert] given parameters are kept and the rest defaulted
? got: ((kdf . argon2id) (memory-kib . 262144) (iterations . 4) (parallelism . 1))
+----------------------------------------------+
[? suite] HMAC-SHA256 (RFC 4231)
[? test] RFC 4231 test case 1
[?? assert] a 20-byte key of 0x0b over "Hi There"
? got: b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7
[? test] RFC 4231 test case 2
[?? assert] the key "Jefe" over its test message
? got: 5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
[? test] the digest is 32 bytes and key-sensitive
[?? assert] changing the key changes the MAC
? got: #f
[?? assert] 64 hexadecimal digits, i.e. 32 bytes
? got: 64
+----------------------------------------------+
[? suite] OpenSSL-compatible sealing
[? test] failures are reported rather than ignored
[?? assert] a truncated body raises rather than returning garbage
? got: #t
[?? assert] input that is not Base64 raises
? got: #t
[?? assert] the wrong key raises for OpenSSL-produced data too
? got: #t
[?? assert] Base64 without a Salted__ header raises
? got: #t
[?? assert] the wrong key raises
? got: #t
[? test] data produced by OpenSSL itself can be opened
[?? assert] an openssl-encrypted string, random salt and all
? got: a secret that openssl sealed
[? test] round-trips
[?? assert] a longer payload round-trips
? got: a secret that openssl sealed
[?? assert] one padded block follows the 16-byte Salted__ header
? got: 32
[?? assert] the empty string round-trips
? got:
[? test] the salt must be 8 bytes
[?? assert] a short salt raises
? success catching error: misc-error: (#f bewaking: a PBKDF2 salt must be 8 bytes long. () #f)
[? test] a fixed-salt vector matches OpenSSL byte for byte
[?? assert] and it decrypts back
? got: bewaking
[?? assert] the ciphertext of a known salt and key
? got: U2FsdGVkX18AAQIDBAUGByAGpafcjamTQUX3eOyvQUE=
+----------------------------------------------+
[? suite] Authenticated payloads (AES-256-GCM)
[? test] the payload is byte for byte the reference implementation's
[?? assert] Argon2id with its parameters recorded, likewise
? got: 020000200000000001000000010707070707070707070707070707070709090909090909090909090996b424d50c29974261cc56650752a5237f7528271202d70046f0bc49c8c0bfefb86150c0b946a554a1590bec6d5f03409bbff197af7d782651
[?? assert] PBKDF2-HMAC-SHA256, as Python's cryptography seals it
? got: 010707070707070707070707070707070709090909090909090909090915d0ed647041883a00a39c1a98153992e43396ca2ef78f30cf603552820a8ed81e320d38a87aee14ad4a7f41b2df7b369d6614500294e59e58
[? test] the tag refuses every alteration
[?? assert] a flipped ciphertext digit is refused
? got: #t
[?? assert] the wrong key is refused
? got: #t
[?? assert] a flipped tag digit is refused
? got: #t
[?? assert] different associated data is refused
? got: #t
[?? assert] a truncated payload is refused
? got: #t
[?? assert] an unknown key-derivation id is refused, not guessed at
? got: #t
[? test] fresh salt and nonce still round-trip
[?? assert] a payload sealed without it does not open with it
? got: #t
[?? assert] and so does an Argon2id one
? got: #t
[?? assert] a payload sealed with random salt and nonce opens
? got: #t
[?? assert] associated data is optional: empty means none
? got: #t
[? test] hexadecimal that stands for bytes decodes to bytes
[?? assert] and keeps the raw octets, which base16-decode cannot
? got: 255
[?? assert] base16->bytes returns a bytevector
? got: #t
[? test] the payload records which derivation to use
[?? assert] PBKDF2 is identified by its own id
? got: 1
[?? assert] Argon2id is identified by a different id
? got: 2
[?? assert] the memory cost is recorded, in KiB
? got: 8192
[?? assert] the lane count is recorded
? got: 1
[?? assert] the pass count is recorded
? got: 1
+----------------------------------------------+
[? suite] Base64 encoding
[? test] decoding is tolerant of line breaks
[?? assert] encoding then decoding is lossless
? got: hello world
[?? assert] a trailing newline is ignored
? got: hello
[? test] malformed input is rejected
[?? assert] non-alphabet characters raise
? success catching error: misc-error: (#f bewaking: malformed base64 input. () #f)
[?? assert] a truncated group raises
? success catching error: misc-error: (#f bewaking: malformed base64 input (bad length or padding). () #f)
[?? assert] an impossible amount of padding raises
? success catching error: misc-error: (#f bewaking: malformed base64 input (bad length or padding). () #f)
[?? assert] data after padding raises
? success catching error: misc-error: (#f bewaking: malformed base64 input (data after padding). () #f)
[? test] RFC 4648 test vectors
[?? assert] five bytes of input
? got: Zm9vYmE=
[?? assert] one byte of input
? got: Zg==
[?? assert] three bytes of input
? got: Zm9v
[?? assert] six bytes of input
? got: Zm9vYmFy
[?? assert] four bytes of input
? got: Zm9vYg==
[?? assert] two bytes of input
? got: Zm8=
[?? assert] empty
? got:
+----------------------------------------------+
[? suite] Sealed payloads (key derivation is recorded)
[? test] sizes and headers are validated
[?? assert] a 16-byte PBKDF2 salt is refused
? got: #t
[?? assert] a payload with no known header is refused
? got: #t
[?? assert] an 8-byte Argon2id salt is refused
? got: #t
[? test] an Argon2id payload records its derivation and parameters
[?? assert] the pass count is recorded
? got: 1
[?? assert] the lane count is recorded
? got: 1
[?? assert] the memory cost is recorded, in KiB
? got: 8192
[?? assert] the header names the derivation
? got: Argon2id
[? test] both readers keep their contracts
[?? assert] openssl-decrypt refuses what the OpenSSL CLI could not read
? got: #t
[?? assert] kdf-decrypt opens payloads produced by OpenSSL
? got: #t
[? test] tampering with the recorded parameters is not honoured
[?? assert] a changed pass count no longer decrypts
? got: #t
[? test] an Argon2id payload round-trips and resists the wrong key
[?? assert] the same key opens it
? got: #t
[?? assert] another key does not
? got: #t
[? test] the default KDF keeps the OpenSSL layout byte for byte
[?? assert] a fixed-salt PBKDF2 payload equals OpenSSL's own output
? got: #t
+----------------------------------------------+
[? suite] Authenticated payload hygiene
[? test] an empty plaintext is still authenticated
[?? assert] the empty plaintext round-trips
? got: #t
[?? assert] even an empty payload carries a salt, a nonce and a tag
? got: #t
[? test] the recorded derivation cost is bounded before it is spent
[?? assert] a memory cost above the cap is refused, not allocated
? got: #t
[?? assert] less than 8 KiB per lane is refused
? got: #t
[?? assert] a lane count above the cap is refused
? got: #t
[?? assert] a cost inside the bounds is derived, then fails the tag: the bound is not a blanket refusal
? got: #t
[?? assert] a zero pass count is refused
? got: #t
[? test] every payload uses a fresh salt and nonce
[?? assert] the recorded nonces differ as well
? got: #f
[? test] the associated data is authenticated but not stored
[?? assert] the associated data does not appear in the payload
? got: #f
[?? assert] one extra associated-data byte breaks the tag
? got: #t
[? test] the salt and the nonce are used, not decorative
[?? assert] editing a salt digit breaks the tag
? got: #t
[?? assert] editing an Argon2id parameter digit is refused
? got: #t
[?? assert] editing a nonce digit breaks the tag
? got: #t
+----------------------------------------------+
[? suite] Derived key material
[? test] an unknown derivation is refused
[?? assert] no other function can be named
? got: #t
[? test] PBKDF2 key material is the PBKDF2 derivation's
[?? assert] the requested length is produced
? got: 32
[?? assert] derive-key-material agrees with the primitive it names
? got: 25ff9d4852ea4ea0ff9caf7aa33ed9e25f8d8a4eb2915445e0ff398292d67473
[? test] Argon2id key material honours its parameters
[?? assert] the full key-and-IV length can be derived
? got: 48
[?? assert] changing the pass count changes the material
? got: #t
[?? assert] changing the passphrase changes the material
? got: #t
+----------------------------------------------+
[? suite] Encodings of raw bytes
[? test] Base16 round-trips arbitrary bytevectors
[?? assert] bytes that are not valid UTF-8 survive the round trip
? got: #t
[?? assert] the empty bytevector round-trips
? got: #t
[? test] Base64 round-trips arbitrary bytevectors
[?? assert] bytes that are not valid UTF-8 survive the round trip
? got: 3
?? info - ran 13 tasks in: 0 seconds, 29669 microseconds
running unit tests from file: web-spec.scm, entrypoint: spec
error decrypting token: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting token: misc-error: (#f ~A (bewaking: malformed BWK3 envelope (created-at)) #f)error decrypting token: misc-error: (#f ~A (open-envelope: not a bewaking envelope (expected BWK3).) #f)error decrypting token: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting object: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting token: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting token: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)
?? info - shuffled with seed 844052010 (replay with VERITAS_SEED=844052010)
+----------------------------------------------+
[? suite] Web chapter: sign-in seals a session into a cookie
[? test] a verified user gets a sealed session cookie
[?? assert] the cookie value is a BWK3 envelope
? got: #t
[?? assert] (c) path "/" applies the cookie to the whole site
? got: /
[?? assert] (a) HttpOnly puts the cookie out of reach of JavaScript
? got: #t
[?? assert] (d) the cookie's lifetime matches #:token-validity-seconds
? got: 604800
[?? assert] the redirect target is the dashboard
? got: /dashboard
[?? assert] (b) Secure keeps it off plain HTTP
? got: #t
[?? assert] the cookie is named as documented
? got: x-bewaking-session
[?? assert] sign-in redirects, as the README's controller does
? got: 302
[? test] the cookie lifetime can be aligned with the token's
[?? assert] the envelope is sealed now
? got: #t
[?? assert] the payload lifetime is separate from the cookie's
? got: #f
[?? assert] the requested lifetime is recorded in the cookie
? got: 86400
[? test] bad credentials get no cookie at all
[?? assert] no session is created for a failed sign-in
? got: #f
[?? assert] the failure is reported, not swallowed
? got: invalid credentials
[?? assert] a wrong password is a plain 401
? got: 401
+----------------------------------------------+
[? suite] Web chapter: sealed objects as stateless state
[? test] a cursor or half-finished form can be sealed the same way
[?? assert] a sealed cursor round-trips
? got: page-3
[?? assert] a client cannot page anywhere it was not sent
? got: #t
[? test] the link answers only to the key that sealed it
[?? assert] a link sealed elsewhere does not open here
? got: #f
[? test] an edited link is worth nothing
[?? assert] and garbage is not a link either
? got: #f
[?? assert] the link was edited in the mail client
? got: #f
[? test] a password-reset link round-trips with no table row
[?? assert] the hour-long lifetime is recorded in the header
? got: #t
[?? assert] the state comes back as an ordinary alist
? got: ((user . u-1) (action . reset-password))
[?? assert] the user id is not readable in the link
? got: #f
[?? assert] the link is a sealed envelope
? got: #t
[? test] an expired link is refused by the server, not by the client
[?? assert] before the expiry the link opens
? got: ((user . u-1) (action . reset-password))
[?? assert] after the expiry the server refuses it
? got: #f
[?? assert] and the header says so, for the log
? got: #t
+----------------------------------------------+
[? suite] Web chapter: auditing what is safe to log
[? test] auditing garbage is #f, not an exception
[?? assert] a malformed BWK3 value yields no metadata
? got: #f
[?? assert] an unparseable cookie yields no metadata
? got: #f
[?? assert] a missing cookie yields no metadata
? got: #f
[? test] an expiry is reported as a time, and its absence as nothing
[?? assert] a token sealed with a TTL records its expiry
? got: #t
[?? assert] and the audit helper agrees it is live
? got: #f
[?? assert] the audit reports no envelope expiry by default
? got: #f
[? test] the envelope header is readable with no keys at all
[?? assert] not the encryption key and not the signing key either
? got: #f
[?? assert] the key id is reported, which is what rotation is keyed on
? got: default
[?? assert] the audit output carries no key material
? got: #f
[?? assert] the version is reported for the log
? got: BWK3
[?? assert] a fresh session is not expired
? got: #f
[?? assert] the creation time is reported
? got: #t
+----------------------------------------------+
[? suite] Web chapter: protecting a route
[? test] the same session works as an API bearer token
[?? assert] the granted permissions are what the token says
? got: (read-only access-files)
[?? assert] the API route admits the header token
? got: 200
[?? assert] the token identifies the caller without a database round-trip
? got: u-1
[? test] a permission that was never granted is refused on the server
[?? assert] the API route answers 401 rather than 403-by-omission
? got: 401
[?? assert] the permission the user does have is enough
? got: #t
[?? assert] a route needing nothing is open to a valid session
? got: #t
[?? assert] the helper returns #f, not a partially authorized payload
? got: #f
[?? assert] the browser route refuses a route the user may not use
? got: 302
[? test] the payload is the identity, not the request
[?? assert] the owner id comes from the sealed payload
? got: u-1
[?? assert] a client-supplied user id is a hint, never the identity
? got: #f
[? test] an expired session is refused, and sign-out clears the cookie
[?? assert] and the helper returns #f for it
? got: #f
[?? assert] sign-out deletes the cookie, the only cleanup there is
? got:
[?? assert] a token whose payload lifetime has passed is refused
? got: 302
[?? assert] the deletion cookie expires immediately
? got: 0
[?? assert] sign-out redirects to the login page
? got: 302
[? test] the signed-in user is admitted
[?? assert] permissions travel inside the token
? got: (read-only access-files)
[?? assert] and its own payload expiry
? got: #t
[?? assert] the browser route runs the handler
? got: 200
[?? assert] the payload carries its own token id
? got: #t
[?? assert] the handler sees the owner id from the payload
? got: u-1
[?? assert] and 'name' came back as 'owner-name'
? got: Joe
[?? assert] the thunk's 'email' came back as 'owner-email'
? got: joe@example.com
[? test] every failure mode is a refusal, never an admission
[?? assert] a keyring that cannot resolve the key id is refused
? got: 302
[?? assert] arbitrary text is refused
? got: 302
[?? assert] a second user's genuine session is admitted, as that user
? got: 200
[?? assert] a cookie that is not a real envelope is refused
? got: 302
[?? assert] an empty cookie is refused
? got: 302
[?? assert] and it admits the *second* user, not the first
? got: u-2
[?? assert] a tampered cookie is refused
? got: 302
[?? assert] a request with no cookie is redirected to /login
? got: 302
+----------------------------------------------+
[? suite] Web chapter: keys, rotation and revocation
[? test] rotation needs no downtime and no forced logout
[?? assert] an old token still opens: nothing to migrate
? got: #t
[?? assert] the key id is embedded in the envelope
? got: #t
[?? assert] and so does a new one
? got: #t
[?? assert] new logins are sealed with the current key
? got: #t
[? test] retiring a key logs every session sealed with it out at once
[?? assert] the retired key id stops opening sessions
? got: #f
[?? assert] the current key keeps working
? got: #t
[? test] keys stay secret and distinct, which is the whole requirement
[?? assert] swapping the two keys does not open the session
? got: #f
[?? assert] the encryption key does not appear in the token
? got: #f
[?? assert] nor does the signing key
? got: #f
?? info - ran 5 tasks in: 0 seconds, 18601 microseconds
running unit tests from file: cli-spec.scm, entrypoint: spec
?? info - shuffled with seed 844052010 (replay with VERITAS_SEED=844052010)
+----------------------------------------------+
[? suite] CLI dispatch
[? test] help and exit codes
[?? assert] a token subcommand is required
? got: 1
[?? assert] an unknown command exits with 1
? got: 1
[?? assert] help documents the keyring option
? got: #t
[?? assert] help documents the rotation metadata options
? got: #t
[?? assert] help lists the token generate command
? got: #t
[?? assert] help prints the usage banner
? got: #t
[?? assert] version exits successfully
? got: 0
[?? assert] help exits successfully
? got: 0
[?? assert] help lists the token inspect command
? got: #t
[?? assert] decrypting without keys fails
? got: 1
+----------------------------------------------+
[? suite] CLI object commands
[? test] object expiry policy
[?? assert] an object sealed with a past expiry is refused
? got: #t
[? test] object round-trip
[?? assert] object encrypt then decrypt round-trips a datum
? got: #t
+----------------------------------------------+
[? suite] CLI rotation metadata
[? test] expiry is sealed into tokens and enforced
[?? assert] --max-age-seconds accepts fresh data
? got: #t
[?? assert] --allow-expired opens it anyway
? got: #t
[?? assert] --ttl-seconds and --expires-at conflict
? got: #t
[?? assert] an expired token exits with 1
? got: #t
[? test] token inspect audits the header without keys
[?? assert] inspect reports an expired envelope
? got: #t
[?? assert] inspect rejects a non-envelope token
? got: #t
[?? assert] inspect reports version, key id and expiry
? got: #t
+----------------------------------------------+
[? suite] CLI token commands
[? test] key id and keyring rotation
[?? assert] a keyring round-trips and rejects the wrong key
? got: #t
[? test] wrong keys are rejected
[?? assert] a wrong signing key exits with 1
? got: #t
[? test] token round-trip
[?? assert] generate then decrypt preserves the payload
? got: #t
+----------------------------------------------+
[? suite] CLI password commands
[? test] password strength
[?? assert] strength exits successfully
? got: 0
[?? assert] a common password is reported weak
? got: #t
[? test] password generate
[?? assert] the requested length is honoured
? got: 32
[?? assert] generation exits successfully
? got: 0
[?? assert] symbols are excluded on request
? got: (lowercase uppercase digit)
+----------------------------------------------+
[? suite] CLI key derivation and dropped formats
[? test] envelopes of dropped formats are refused
[?? assert] a BWK2 token cannot be decrypted or inspected
? got: #t
[?? assert] a BWK1 token cannot be decrypted or inspected
? got: #t
[?? assert] an empty token is refused
? got: #t
[? test] the key derivation is chosen when sealing and read back when opening
[?? assert] a KDF parameter without --kdf is refused
? got: #t
[?? assert] an Argon2id token round-trips through the CLI
? got: #t
[? test] a refusal does not reveal which key ids the keyring configures
[?? assert] an unknown key id and an edited body give the same message and exit code
? got: #t
[? test] tampering and missing arguments are refused
[?? assert] sealing an object without keys exits with 1
? got: #t
[?? assert] generating without an owner id exits with 1
? got: #t
[?? assert] an edited token exits with 1
? got: #t
?? info - ran 6 tasks in: 0 seconds, 10761 microseconds
running unit tests from file: password-spec.scm, entrypoint: spec
?? info - shuffled with seed 844052010 (replay with VERITAS_SEED=844052010)
+----------------------------------------------+
[? suite] Password character classes
[? test] password-character-classes
[?? assert] digits only
? got: (digit)
[?? assert] an empty password has no classes
? got: ()
[?? assert] uppercase letters only
? got: (uppercase)
[?? assert] all four classes are detected
? got: (lowercase uppercase digit symbol)
[?? assert] lowercase letters only
? got: (lowercase)
+----------------------------------------------+
[? suite] Password entropy
[? test] password-entropy
[?? assert] an empty password has zero entropy
? got: 0
[?? assert] eight lowercase letters are ~37.6 bits
? got: #t
[?? assert] adding a character class increases the entropy
? got: #t
+----------------------------------------------+
[? suite] Password strength
[? test] password-strength
[?? assert] sequential runs are flagged in the feedback
? got: #t
[?? assert] common passwords get a specific warning
? got: #t
[? test] strong-password?
[?? assert] min-score lowers the bar
? got: #t
[?? assert] a common word is not strong
? got: #f
[?? assert] a long passphrase is strong
? got: #t
[?? assert] a short mixed password is not strong
? got: #f
+----------------------------------------------+
[? suite] Password generation
[? test] generate-strong-password
[?? assert] too-short a request raises an error
? success catching error: misc-error: (#f ~A (generate-strong-password: length must be at least 4 to cover every character class.) #f)
[?? assert] length is respected
? got: 32
[?? assert] symbols can be excluded
? got: (lowercase uppercase digit)
[?? assert] default generated passwords use every class
? got: (lowercase uppercase digit symbol)
?? info - ran 4 tasks in: 0 seconds, 5204 microseconds
finished running tests
;;; note: source file ./src/bewaking/bewaking.scm
;;; newer than compiled /gnu/store/1xfphns7yvbizg1xwfnqigbnl3mrvfx7-profile/lib/guile/3.0/site-ccache/bewaking/bewaking.go
;;; found fresh local cache at /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/src/bewaking/bewaking.scm.go
;;; note: source file ./src/bewaking/crypto.scm
;;; newer than compiled /gnu/store/1xfphns7yvbizg1xwfnqigbnl3mrvfx7-profile/lib/guile/3.0/site-ccache/bewaking/crypto.go
;;; found fresh local cache at /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/src/bewaking/crypto.scm.go
;;; note: source file ./src/bewaking/cli.scm
;;; newer than compiled /gnu/store/1xfphns7yvbizg1xwfnqigbnl3mrvfx7-profile/lib/guile/3.0/site-ccache/bewaking/cli.go
;;; found fresh local cache at /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/src/bewaking/cli.scm.go
;;; note: source file ./src/bewaking/password.scm
;;; newer than compiled /gnu/store/1xfphns7yvbizg1xwfnqigbnl3mrvfx7-profile/lib/guile/3.0/site-ccache/bewaking/password.go
;;; found fresh local cache at /root/.cache/guile/ccache/3.0-LE-8-4.7/etc/byggsteg/job-clone/bewaking/trunk/src/bewaking/password.scm.go
--------------------------------------------------------------
Maak: the infinitely extensible command runner
Loading tasks from Maak file: /etc/byggsteg/job-clone/bewaking/trunk/maak.scm
Executing Maak tasks: test
--------------------------------------------------------------
=> Running Maak task: test
Run project's tests.
→ running command: cd /etc/byggsteg/job-clone/bewaking/trunk && sudo maak deploy
API documentation saved to doc/api
index.texi: warning: must specify a title with a title command or @top
renamed './index' -> './doc/api-dist'
removed '/srv/http/jointhefreeworld.org/api-docs/bewaking/API.html'
removed '/srv/http/jointhefreeworld.org/api-docs/bewaking/bewaking_005fbewaking.html'
removed '/srv/http/jointhefreeworld.org/api-docs/bewaking/bewaking_005fcli.html'
removed '/srv/http/jointhefreeworld.org/api-docs/bewaking/bewaking_005fcrypto.html'
removed '/srv/http/jointhefreeworld.org/api-docs/bewaking/bewaking_005fpassword.html'
removed directory '/srv/http/jointhefreeworld.org/api-docs/bewaking'
mkdir: created directory '/srv/http/jointhefreeworld.org/api-docs/bewaking'
renamed 'doc/api-dist/API.html' -> '/srv/http/jointhefreeworld.org/api-docs/bewaking/API.html'
renamed 'doc/api-dist/bewaking_005fbewaking.html' -> '/srv/http/jointhefreeworld.org/api-docs/bewaking/bewaking_005fbewaking.html'
renamed 'doc/api-dist/bewaking_005fcli.html' -> '/srv/http/jointhefreeworld.org/api-docs/bewaking/bewaking_005fcli.html'
renamed 'doc/api-dist/bewaking_005fcrypto.html' -> '/srv/http/jointhefreeworld.org/api-docs/bewaking/bewaking_005fcrypto.html'
renamed 'doc/api-dist/bewaking_005fpassword.html' -> '/srv/http/jointhefreeworld.org/api-docs/bewaking/bewaking_005fpassword.html'
removed 'doc/api/index.texi'
removed directory 'doc/api'
removed directory 'doc/api-dist'
removed directory 'doc'
--------------------------------------------------------------
Maak: the infinitely extensible command runner
Loading tasks from Maak file: /etc/byggsteg/job-clone/bewaking/trunk/maak.scm
Executing Maak tasks: deploy
--------------------------------------------------------------
=> Running Maak task: deploy
Generate documentation for the project and publish to web directory.