From 3579b94e836bd61605b38039264fcd5358e5ffe3 Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Thu, 6 Nov 2025 07:56:12 -0600 Subject: [PATCH] Add tests for all functions of each algorithm against AEGIS test vectors (JSON). Incremental updates are tested with randomized splits to catch a variety of errors. --- tests/__init__.py | 0 .../test-vectors/aegis-128l-test-vectors.json | 113 ++++++++ .../aegis-128x2-test-vectors.json | 48 ++++ .../aegis-128x4-test-vectors.json | 66 +++++ .../test-vectors/aegis-256-test-vectors.json | 108 ++++++++ .../aegis-256x2-test-vectors.json | 44 +++ .../aegis-256x4-test-vectors.json | 58 ++++ tests/test-vectors/aegismac-test-vectors.json | 58 ++++ tests/test-vectors/aesround-test-vector.json | 8 + tests/test-vectors/gen.py | 89 ++++++ tests/test_encrypt.py | 253 ++++++++++++++++++ tests/test_mac.py | 106 ++++++++ tests/util.py | 28 ++ 13 files changed, 979 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/test-vectors/aegis-128l-test-vectors.json create mode 100644 tests/test-vectors/aegis-128x2-test-vectors.json create mode 100644 tests/test-vectors/aegis-128x4-test-vectors.json create mode 100644 tests/test-vectors/aegis-256-test-vectors.json create mode 100644 tests/test-vectors/aegis-256x2-test-vectors.json create mode 100644 tests/test-vectors/aegis-256x4-test-vectors.json create mode 100644 tests/test-vectors/aegismac-test-vectors.json create mode 100644 tests/test-vectors/aesround-test-vector.json create mode 100755 tests/test-vectors/gen.py create mode 100644 tests/test_encrypt.py create mode 100644 tests/test_mac.py create mode 100644 tests/util.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test-vectors/aegis-128l-test-vectors.json b/tests/test-vectors/aegis-128l-test-vectors.json new file mode 100644 index 0000000..83dbb16 --- /dev/null +++ b/tests/test-vectors/aegis-128l-test-vectors.json @@ -0,0 +1,113 @@ +[ + { + "name": "Update Test Vector", + "S0": "9b7e60b24cc873ea894ecc07911049a3", + "S1": "330be08f35300faa2ebf9a7b0d274658", + "S2": "7bbd5bd2b049f7b9b515cf26fbe7756c", + "S3": "c35a00f55ea86c3886ec5e928f87db18", + "S4": "9ebccafce87cab446396c4334592c91f", + "S5": "58d83e31f256371e60fc6bb257114601", + "S6": "1639b56ea322c88568a176585bc915de", + "S7": "640818ffb57dc0fbc2e72ae93457e39a", + "M0": "033e6975b94816879e42917650955aa0", + "M1": "fcc1968a46b7e97861bd6e89af6aa55f", + "S0_2": "596ab773e4433ca0127c73f60536769d", + "S1_2": "790394041a3d26ab697bde865014652d", + "S2_2": "38cf49e4b65248acd533041b64dd0611", + "S3_2": "16d8e58748f437bfff1797f780337cee", + "S4_2": "9689ecdf08228c74d7e3360cca53d0a5", + "S5_2": "a21746bb193a569e331e1aa985d0d729", + "S6_2": "09d714e6fcf9177a8ed1cde7e3d259a6", + "S7_2": "61279ba73167f0ab76f0a11bf203bdff" + }, + { + "name": "Test Vector 1", + "key": "10010000000000000000000000000000", + "nonce": "10000200000000000000000000000000", + "ad": "", + "msg": "00000000000000000000000000000000", + "ct": "c1c0e58bd913006feba00f4b3cc3594e", + "tag128": "abe0ece80c24868a226a35d16bdae37a", + "tag256": "25835bfbb21632176cf03840687cb968cace4617af1bd0f7d064c639a5c79ee4" + }, + { + "name": "Test Vector 2", + "key": "10010000000000000000000000000000", + "nonce": "10000200000000000000000000000000", + "ad": "", + "msg": "", + "ct": "", + "tag128": "c2b879a67def9d74e6c14f708bbcc9b4", + "tag256": "1360dc9db8ae42455f6e5b6a9d488ea4f2184c4e12120249335c4ee84bafe25d" + }, + { + "name": "Test Vector 3", + "key": "10010000000000000000000000000000", + "nonce": "10000200000000000000000000000000", + "ad": "0001020304050607", + "msg": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "ct": "79d94593d8c2119d7e8fd9b8fc77845c5c077a05b2528b6ac54b563aed8efe84", + "tag128": "cc6f3372f6aa1bb82388d695c3962d9a", + "tag256": "022cb796fe7e0ae1197525ff67e309484cfbab6528ddef89f17d74ef8ecd82b3" + }, + { + "name": "Test Vector 4", + "key": "10010000000000000000000000000000", + "nonce": "10000200000000000000000000000000", + "ad": "0001020304050607", + "msg": "000102030405060708090a0b0c0d", + "ct": "79d94593d8c2119d7e8fd9b8fc77", + "tag128": "5c04b3dba849b2701effbe32c7f0fab7", + "tag256": "86f1b80bfb463aba711d15405d094baf4a55a15dbfec81a76f35ed0b9c8b04ac" + }, + { + "name": "Test Vector 5", + "key": "10010000000000000000000000000000", + "nonce": "10000200000000000000000000000000", + "ad": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223242526272829", + "msg": "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637", + "ct": "b31052ad1cca4e291abcf2df3502e6bdb1bfd6db36798be3607b1f94d34478aa7ede7f7a990fec10", + "tag128": "7542a745733014f9474417b337399507", + "tag256": "b91e2947a33da8bee89b6794e647baf0fc835ff574aca3fc27c33be0db2aff98" + }, + { + "name": "Test Vector 6", + "error": "verification failed", + "key": "10000200000000000000000000000000", + "nonce": "10010000000000000000000000000000", + "ad": "0001020304050607", + "ct": "79d94593d8c2119d7e8fd9b8fc77", + "tag128": "5c04b3dba849b2701effbe32c7f0fab7", + "tag256": "86f1b80bfb463aba711d15405d094baf4a55a15dbfec81a76f35ed0b9c8b04ac" + }, + { + "name": "Test Vector 7", + "error": "verification failed", + "key": "10010000000000000000000000000000", + "nonce": "10000200000000000000000000000000", + "ad": "0001020304050607", + "ct": "79d94593d8c2119d7e8fd9b8fc78", + "tag128": "5c04b3dba849b2701effbe32c7f0fab7", + "tag256": "86f1b80bfb463aba711d15405d094baf4a55a15dbfec81a76f35ed0b9c8b04ac" + }, + { + "name": "Test Vector 8", + "error": "verification failed", + "key": "10010000000000000000000000000000", + "nonce": "10000200000000000000000000000000", + "ad": "0001020304050608", + "ct": "79d94593d8c2119d7e8fd9b8fc77", + "tag128": "5c04b3dba849b2701effbe32c7f0fab7", + "tag256": "86f1b80bfb463aba711d15405d094baf4a55a15dbfec81a76f35ed0b9c8b04ac" + }, + { + "name": "Test Vector 9", + "error": "verification failed", + "key": "10010000000000000000000000000000", + "nonce": "10000200000000000000000000000000", + "ad": "0001020304050607", + "ct": "79d94593d8c2119d7e8fd9b8fc77", + "tag128": "6c04b3dba849b2701effbe32c7f0fab8", + "tag256": "86f1b80bfb463aba711d15405d094baf4a55a15dbfec81a76f35ed0b9c8b04ad" + } +] \ No newline at end of file diff --git a/tests/test-vectors/aegis-128x2-test-vectors.json b/tests/test-vectors/aegis-128x2-test-vectors.json new file mode 100644 index 0000000..eaa1f60 --- /dev/null +++ b/tests/test-vectors/aegis-128x2-test-vectors.json @@ -0,0 +1,48 @@ +[ + { + "name": "Initial State", + "key": "000102030405060708090a0b0c0d0e0f", + "nonce": "101112131415161718191a1b1c1d1e1f", + "ctx[0]": "00010000000000000000000000000000", + "ctx[1]": "01010000000000000000000000000000" + }, + { + "name": "Initial State (after initialization)", + "V[0,0]": "a4fc1ad9a72942fb88bd2cabbba6509a", + "V[0,1]": "80a40e392fc71084209b6c3319bdc6cc", + "V[1,0]": "380f435cf801763b1f0c2a2f7212052d", + "V[1,1]": "73796607b59b1b650ee91c152af1f18a", + "V[2,0]": "6ee1de433ea877fa33bc0782abff2dcb", + "V[2,1]": "b9fab2ab496e16d1facaffd5453cbf14", + "V[3,0]": "85f94b0d4263bfa86fdf45a603d8b6ac", + "V[3,1]": "90356c8cadbaa2c969001da02e3feca0", + "V[4,0]": "09bd69ad3730174bcd2ce9a27cd1357e", + "V[4,1]": "e610b45125796a4fcf1708cef5c4f718", + "V[5,0]": "fcdeb0cf0a87bf442fc82383ddb0f6d6", + "V[5,1]": "61ad32a4694d6f3cca313a2d3f4687aa", + "V[6,0]": "571c207988659e2cdfbdaae77f4f37e3", + "V[6,1]": "32e6094e217573bf91fb28c145a3efa8", + "V[7,0]": "ca549badf8faa58222412478598651cf", + "V[7,1]": "3407279a54ce76d2e2e8a90ec5d108eb" + }, + { + "name": "Test Vector 1", + "key": "000102030405060708090a0b0c0d0e0f", + "nonce": "101112131415161718191a1b1c1d1e1f", + "ad": "", + "msg": "", + "ct": "", + "tag128": "63117dc57756e402819a82e13eca8379", + "tag256": "b92c71fdbd358b8a4de70b27631ace90cffd9b9cfba82028412bac41b4f53759" + }, + { + "name": "Test Vector 2", + "key": "000102030405060708090a0b0c0d0e0f", + "nonce": "101112131415161718191a1b1c1d1e1f", + "ad": "0102030401020304", + "msg": "040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607", + "ct": "5795544301997f93621b278809d6331b3bfa6f18e90db12c4aa35965b5e98c5fc6fb4e54bcb6111842c20637252eff747cb3a8f85b37de80919a589fe0f24872bc926360696739e05520647e390989e1eb5fd42f99678a0276a498f8c454761c9d6aacb647ad56be62b29c22cd4b5761b38f43d5a5ee062f", + "tag128": "1aebc200804f405cab637f2adebb6d77", + "tag256": "c471876f9b4978c44f2ae1ce770cdb11a094ee3feca64e7afcd48bfe52c60eca" + } +] \ No newline at end of file diff --git a/tests/test-vectors/aegis-128x4-test-vectors.json b/tests/test-vectors/aegis-128x4-test-vectors.json new file mode 100644 index 0000000..ba00fbe --- /dev/null +++ b/tests/test-vectors/aegis-128x4-test-vectors.json @@ -0,0 +1,66 @@ +[ + { + "name": "Initial State", + "key": "000102030405060708090a0b0c0d0e0f", + "nonce": "101112131415161718191a1b1c1d1e1f", + "ctx[0]": "00030000000000000000000000000000", + "ctx[1]": "01030000000000000000000000000000", + "ctx[2]": "02030000000000000000000000000000", + "ctx[3]": "03030000000000000000000000000000" + }, + { + "name": "Initial State (after initialization)", + "V[0,0]": "924eb07635003a37e6c6575ba8ce1929", + "V[0,1]": "c8b6a5d91475445e936d48e794be0ce2", + "V[0,2]": "fcd37d050e24084befe3bbb219d64760", + "V[0,3]": "2e9f58cfb893a8800220242c373a8b18", + "V[1,0]": "1a1f60c4fab64e5471dc72edfcf6fe6b", + "V[1,1]": "c1e525ebea2d6375a9edd045dce96381", + "V[1,2]": "97a3e25abd228a44d4a14a6d3fe9185c", + "V[1,3]": "c2d4cf7f4287a98744645674265d4ca8", + "V[2,0]": "7bb50c534f6ec4780530ff1cce8a16e8", + "V[2,1]": "7b08d57557da0b5ef7b5f7d98b0ba189", + "V[2,2]": "6bfcac34ddb68404821a4d665303cb0f", + "V[2,3]": "d95626f6dfad1aed7467622c38529932", + "V[3,0]": "af339fd2d50ee45fc47665c647cf6586", + "V[3,1]": "d0669b39d140f0e118a4a511efe2f95a", + "V[3,2]": "7a94330f35c194fadda2a87e42cdeccc", + "V[3,3]": "233b640d1f4d56e2757e72c1a9d8ecb1", + "V[4,0]": "9f93737d699ba05c11e94f2b201bef5e", + "V[4,1]": "61caf387cf7cfd3f8300ac7680ccfd76", + "V[4,2]": "5825a671ecef03b7a9c98a601ae32115", + "V[4,3]": "87a1fe4d558161a8f4c38731f3223032", + "V[5,0]": "7a5aca78d636c05bbc702b2980196ab6", + "V[5,1]": "915d868408495d07eb527789f282c575", + "V[5,2]": "d0947bfbc1d3309cdffc9be1503aea62", + "V[5,3]": "8834ea57a15b9fbdc0245464a4b8cbef", + "V[6,0]": "e46f4cf71a95ac45b6f0823e3aba1a86", + "V[6,1]": "8c4ecef682fc44a8eba911b3fc7d99f9", + "V[6,2]": "a4fb61e2c928a2ca760b8772f2ea5f2e", + "V[6,3]": "3d34ea89da73caa3016c280500a155a3", + "V[7,0]": "85075f0080e9d618e7eb40f57c32d9f7", + "V[7,1]": "d2ab2b320c6e93b155a3787cb83e5281", + "V[7,2]": "0b3af0250ae36831a1b072e499929bcb", + "V[7,3]": "5cce4d00329d69f1aae36aa541347512" + }, + { + "name": "Test Vector 1", + "key": "000102030405060708090a0b0c0d0e0f", + "nonce": "101112131415161718191a1b1c1d1e1f", + "ad": "", + "msg": "", + "ct": "", + "tag128": "5bef762d0947c00455b97bb3af30dfa3", + "tag256": "a4b25437f4be93cfa856a2f27e4416b42cac79fd4698f2cdbe6af25673e10a68" + }, + { + "name": "Test Vector 2", + "key": "000102030405060708090a0b0c0d0e0f", + "nonce": "101112131415161718191a1b1c1d1e1f", + "ad": "0102030401020304", + "msg": "040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607", + "ct": "e836118562f4479c9d35c17356a833114c21f9aa39e4dda5e5c87f4152a00fce9a7c38f832eafe8b1c12f8a7cf12a81a1ad8a9c24ba9dedfbdaa586ffea67ddc801ea97d9ab4a872f42d0e352e2713dacd609f9442c17517c5a29daf3e2a3fac4ff6b1380c4e46df7b086af6ce6bc1ed594b8dd64aed2a7e", + "tag128": "0e56ab94e2e85db80f9d54010caabfb4", + "tag256": "69abf0f64a137dd6e122478d777e98bc422823006cf57f5ee822dd78397230b2" + } +] \ No newline at end of file diff --git a/tests/test-vectors/aegis-256-test-vectors.json b/tests/test-vectors/aegis-256-test-vectors.json new file mode 100644 index 0000000..dafc932 --- /dev/null +++ b/tests/test-vectors/aegis-256-test-vectors.json @@ -0,0 +1,108 @@ +[ + { + "name": "Update Test Vector", + "S0": "1fa1207ed76c86f2c4bb40e8b395b43e", + "S1": "b44c375e6c1e1978db64bcd12e9e332f", + "S2": "0dab84bfa9f0226432ff630f233d4e5b", + "S3": "d7ef65c9b93e8ee60c75161407b066e7", + "S4": "a760bb3da073fbd92bdc24734b1f56fb", + "S5": "a828a18d6a964497ac6e7e53c5f55c73", + "M": "b165617ed04ab738afb2612c6d18a1ec", + "S0_2": "e6bc643bae82dfa3d991b1b323839dcd", + "S1_2": "648578232ba0f2f0a3677f617dc052c3", + "S2_2": "ea788e0e572044a46059212dd007a789", + "S3_2": "2f1498ae19b80da13fba698f088a8590", + "S4_2": "a54c2ee95e8c2a2c3dae2ec743ae6b86", + "S5_2": "a3240fceb68e32d5d114df1b5363ab67" + }, + { + "name": "Test Vector 1", + "key": "1001000000000000000000000000000000000000000000000000000000000000", + "nonce": "1000020000000000000000000000000000000000000000000000000000000000", + "ad": "", + "msg": "00000000000000000000000000000000", + "ct": "754fc3d8c973246dcc6d741412a4b236", + "tag128": "3fe91994768b332ed7f570a19ec5896e", + "tag256": "1181a1d18091082bf0266f66297d167d2e68b845f61a3b0527d31fc7b7b89f13" + }, + { + "name": "Test Vector 2", + "key": "1001000000000000000000000000000000000000000000000000000000000000", + "nonce": "1000020000000000000000000000000000000000000000000000000000000000", + "ad": "", + "msg": "", + "ct": "", + "tag128": "e3def978a0f054afd1e761d7553afba3", + "tag256": "6a348c930adbd654896e1666aad67de989ea75ebaa2b82fb588977b1ffec864a" + }, + { + "name": "Test Vector 3", + "key": "1001000000000000000000000000000000000000000000000000000000000000", + "nonce": "1000020000000000000000000000000000000000000000000000000000000000", + "ad": "0001020304050607", + "msg": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "ct": "f373079ed84b2709faee373584585d60accd191db310ef5d8b11833df9dec711", + "tag128": "8d86f91ee606e9ff26a01b64ccbdd91d", + "tag256": "b7d28d0c3c0ebd409fd22b44160503073a547412da0854bfb9723020dab8da1a" + }, + { + "name": "Test Vector 4", + "key": "1001000000000000000000000000000000000000000000000000000000000000", + "nonce": "1000020000000000000000000000000000000000000000000000000000000000", + "ad": "0001020304050607", + "msg": "000102030405060708090a0b0c0d", + "ct": "f373079ed84b2709faee37358458", + "tag128": "c60b9c2d33ceb058f96e6dd03c215652", + "tag256": "8c1cc703c81281bee3f6d9966e14948b4a175b2efbdc31e61a98b4465235c2d9" + }, + { + "name": "Test Vector 5", + "key": "1001000000000000000000000000000000000000000000000000000000000000", + "nonce": "1000020000000000000000000000000000000000000000000000000000000000", + "ad": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223242526272829", + "msg": "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637", + "ct": "57754a7d09963e7c787583a2e7b859bb24fa1e04d49fd550b2511a358e3bca252a9b1b8b30cc4a67", + "tag128": "ab8a7d53fd0e98d727accca94925e128", + "tag256": "a3aca270c006094d71c20e6910b5161c0826df233d08919a566ec2c05990f734" + }, + { + "name": "Test Vector 6", + "error": "verification failed", + "key": "1000020000000000000000000000000000000000000000000000000000000000", + "nonce": "1001000000000000000000000000000000000000000000000000000000000000", + "ad": "0001020304050607", + "ct": "f373079ed84b2709faee37358458", + "tag128": "c60b9c2d33ceb058f96e6dd03c215652", + "tag256": "8c1cc703c81281bee3f6d9966e14948b4a175b2efbdc31e61a98b4465235c2d9" + }, + { + "name": "Test Vector 7", + "error": "verification failed", + "key": "1001000000000000000000000000000000000000000000000000000000000000", + "nonce": "1000020000000000000000000000000000000000000000000000000000000000", + "ad": "0001020304050607", + "ct": "f373079ed84b2709faee37358459", + "tag128": "c60b9c2d33ceb058f96e6dd03c215652", + "tag256": "8c1cc703c81281bee3f6d9966e14948b4a175b2efbdc31e61a98b4465235c2d9" + }, + { + "name": "Test Vector 8", + "error": "verification failed", + "key": "1001000000000000000000000000000000000000000000000000000000000000", + "nonce": "1000020000000000000000000000000000000000000000000000000000000000", + "ad": "0001020304050608", + "ct": "f373079ed84b2709faee37358458", + "tag128": "c60b9c2d33ceb058f96e6dd03c215652", + "tag256": "8c1cc703c81281bee3f6d9966e14948b4a175b2efbdc31e61a98b4465235c2d9" + }, + { + "name": "Test Vector 9", + "error": "verification failed", + "key": "1001000000000000000000000000000000000000000000000000000000000000", + "nonce": "1000020000000000000000000000000000000000000000000000000000000000", + "ad": "0001020304050607", + "ct": "f373079ed84b2709faee37358458", + "tag128": "c60b9c2d33ceb058f96e6dd03c215653", + "tag256": "8c1cc703c81281bee3f6d9966e14948b4a175b2efbdc31e61a98b4465235c2da" + } +] \ No newline at end of file diff --git a/tests/test-vectors/aegis-256x2-test-vectors.json b/tests/test-vectors/aegis-256x2-test-vectors.json new file mode 100644 index 0000000..c4f6c56 --- /dev/null +++ b/tests/test-vectors/aegis-256x2-test-vectors.json @@ -0,0 +1,44 @@ +[ + { + "name": "Initial State", + "key": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "nonce": "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f", + "ctx[0]": "00010000000000000000000000000000", + "ctx[1]": "01010000000000000000000000000000" + }, + { + "name": "Initial State (after initialization)", + "V[0,0]": "eca2bf4538442e8712d4972595744039", + "V[0,1]": "201405efa9264f07911db58101903087", + "V[1,0]": "3e536a998799408a97f3479a6f779d48", + "V[1,1]": "0d79a7d822a5d215f78c3bf2feb33ae1", + "V[2,0]": "cf8c63d6f2b4563cdd9231107c85950e", + "V[2,1]": "78d17ed7d8d563ff11bd202c76864839", + "V[3,0]": "d7e0707e6bfbbad913bc94b6993a9fa0", + "V[3,1]": "097e4b1bff40d4c19cb29dfd125d62f2", + "V[4,0]": "a373cf6d537dd66bc0ef0f2f9285359f", + "V[4,1]": "c0d0ae0c48f9df3faaf0e7be7768c326", + "V[5,0]": "9f76560dcae1efacabdcce446ae283bc", + "V[5,1]": "bd52a6b9c8f976a26ec1409df19e8bfe" + }, + { + "name": "Test Vector 1", + "key": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "nonce": "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f", + "ad": "", + "msg": "", + "ct": "", + "tag128": "62cdbab084c83dacdb945bb446f049c8", + "tag256": "25d7e799b49a80354c3f881ac2f1027f471a5d293052bd9997abd3ae84014bb7" + }, + { + "name": "Test Vector 2", + "key": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "nonce": "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f", + "ad": "0102030401020304", + "msg": "040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607", + "ct": "72120c2ea8236180d67859001f4729077b7064c414384fe3a7b52f1571f4f8a7d0f01e18db4f3bc0adb150702e5d147a8d36522132761b994c1bd395589e2ccf0790dfe2a3d12d61cd666b2859827739db4037dd3124c78424459376f6cac08e1a7223a2a43e398ce6385cd654a19f481cba3b8f25910b42", + "tag128": "635d391828520bf1512763f0c8f5cdbd", + "tag256": "b5668d3317159e9cc5d46e4803c3a76ad63bb42b3f47956d94f30db8cb366ad7" + } +] \ No newline at end of file diff --git a/tests/test-vectors/aegis-256x4-test-vectors.json b/tests/test-vectors/aegis-256x4-test-vectors.json new file mode 100644 index 0000000..c91638a --- /dev/null +++ b/tests/test-vectors/aegis-256x4-test-vectors.json @@ -0,0 +1,58 @@ +[ + { + "name": "Initial State", + "key": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "nonce": "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f", + "ctx[0]": "00030000000000000000000000000000", + "ctx[1]": "01030000000000000000000000000000", + "ctx[2]": "02030000000000000000000000000000", + "ctx[3]": "03030000000000000000000000000000" + }, + { + "name": "Initial State (after initialization)", + "V[0,0]": "482a86e8436cd2361063a4b2702769b9", + "V[0,1]": "d95a2be81c9245b22996f68eea0122f9", + "V[0,2]": "0c2a3b348b1a5e256c6751377318c41e", + "V[0,3]": "f64436a21653fe7cf2e0829a177db383", + "V[1,0]": "e705e8866267717d96092e58e78b574c", + "V[1,1]": "d1dd412142df9806cc267af2fe1d830e", + "V[1,2]": "30e7dfd3c9941b8394e95bdf5bac99d9", + "V[1,3]": "9f27186f8a4fab86820689822c3c74d2", + "V[2,0]": "e1aa6af5d9e31dde8d94a48a0810fa89", + "V[2,1]": "63555cdf0d98f18fb75b029ad80786c0", + "V[2,2]": "a3ee0e4a3429a9539e4fcec385475608", + "V[2,3]": "28ea527d31ef61df498dc107fe02df99", + "V[3,0]": "37f06808410c8f3954525ae44584d3be", + "V[3,1]": "8fcc23bca2fe2209f93d34e2da35b33d", + "V[3,2]": "33156347df89eaa69ab11096362daccf", + "V[3,3]": "bbe58d9dbe8c5b0469be5a87086db5d4", + "V[4,0]": "d1c9eb37fecbc5ada7b351fa4f501f32", + "V[4,1]": "0b9b803283c1538628b507c8f6432434", + "V[4,2]": "bfb8b6d4f87cce28825c7e92f54b8728", + "V[4,3]": "8917bb5b09c32f900c6a5a1d63c46264", + "V[5,0]": "4f6110c2ef0c3c687e90c1e5532ddf8e", + "V[5,1]": "031bd85d99f64684d23728a0453c72a1", + "V[5,2]": "10bc7ec34d4119b5bdeb6c7dfc458247", + "V[5,3]": "591ece530aeaa5c9867220156f5c25e3" + }, + { + "name": "Test Vector 1", + "key": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "nonce": "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f", + "ad": "", + "msg": "", + "ct": "", + "tag128": "3b7fee6cee7bf17888ad11ed2397beb4", + "tag256": "6093a1a8aab20ec635dc1ca71745b01b5bec4fc444c9ffbebd710d4a34d20eaf" + }, + { + "name": "Test Vector 2", + "key": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "nonce": "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f", + "ad": "0102030401020304", + "msg": "040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607040506070405060704050607", + "ct": "bfc2085b7e8017da99b0b6d646ae4d01f4ba8f2e7dfca1d759ae48a135139b9aaac6b4f5db810d426be1fdaff4e1454153a34b11da78ed7e418ee2ee9853042e95536aecbb694cea1b16a478eb0d4d1bf6509b1ce652a45af58e0e46ffccfa2d0426e702391d2ff5813808b81748a490dd656465fed61f09", + "tag128": "b63b611b13975e2f3dc3cb6c2397bfcd", + "tag256": "7847eace74409ee56c8f4cf63a9c2841ce7c8bd567d7c0ca514c879a190b978c" + } +] \ No newline at end of file diff --git a/tests/test-vectors/aegismac-test-vectors.json b/tests/test-vectors/aegismac-test-vectors.json new file mode 100644 index 0000000..6d8bfc0 --- /dev/null +++ b/tests/test-vectors/aegismac-test-vectors.json @@ -0,0 +1,58 @@ +[ + { + "name": "AEGISMAC-128L Test Vector", + "key": "10010000000000000000000000000000", + "nonce": "10000200000000000000000000000000", + "data": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122", + "tag128": "d3f09b2842ad301687d6902c921d7818", + "tag256": "9490e7c89d420c9f37417fa625eb38e8cad53c5cbec55285e8499ea48377f2a3" + }, + { + "name": "AEGISMAC-128X2 Test Vector", + "key": "10010000000000000000000000000000", + "nonce": "10000200000000000000000000000000", + "data": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122", + "tags128": "9f5f69928fa481fa86e8a51e072a9b29eeaa77a356f796b427f6a54f52ae0e20", + "tag128": "6873ee34e6b5c59143b6d35c5e4f2c6e", + "tags256": "22cdcf558d0338b6ad8fbba4da7307d30bd685fff23dc9d41f598c2a7ea44055", + "tag256": "afcba3fc2d63c8d6c7f2d63f3ec8fbbbaf022e15ac120e78ffa7755abccd959c" + }, + { + "name": "AEGISMAC-128X4 Test Vector", + "key": "10010000000000000000000000000000", + "nonce": "10000200000000000000000000000000", + "data": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122", + "tags128": "7fecd913a7cb0011b6c4c88e0c6f857819a98fbeaf21d1092c32953fff82c8a9c7b5e6625a5765d04af26cf22adc12824c8cf3b4dbb85f379e13b04a8d06bca7", + "tag128": "c45a98fd9ab8956ce616eb008cfe4e53", + "tags256": "d595732bdf230a1441978414cd8cfa39ecef6ad0ee1e65ae530006ca5d5f4481f9ec5edfa64e9c3d76d3a5eda9fe5bd1fb9d842373f7c90bedb8bfe383740b231264a15143eb8c3d9f17754099f147e3401c83c0d5afc70fd0d68bfd17f9280f", + "tag256": "26fdc76f41b1da7aec7779f6e964beae8904e662f05aca8345ae3befb357412a" + }, + { + "name": "AEGISMAC-256 Test Vector", + "key": "1001000000000000000000000000000000000000000000000000000000000000", + "nonce": "1000020000000000000000000000000000000000000000000000000000000000", + "data": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122", + "tag128": "c08e20cfc56f27195a46c9cef5c162d4", + "tag256": "a5c906ede3d69545c11e20afa360b221f936e946ed2dba3d7c75ad6dc2784126" + }, + { + "name": "AEGISMAC-256X2 Test Vector", + "key": "1001000000000000000000000000000000000000000000000000000000000000", + "nonce": "1000020000000000000000000000000000000000000000000000000000000000", + "data": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122", + "tags128": "db8852ea2c03f22b0d0694ea4e88e4b1", + "tag128": "fb319cb6dd728a764606fb14d37f2a5e", + "tags256": "b4d124976b34b2aa8bc3fa0b55396cf7fb83f4ef5ba607681cddf5ba3e925727", + "tag256": "0844b20ed5147ceae89c7a160263afd4b1382d6b154ecf560ce8a342cb6a8fd1" + }, + { + "name": "AEGISMAC-256X4 Test Vector", + "key": "1001000000000000000000000000000000000000000000000000000000000000", + "nonce": "1000020000000000000000000000000000000000000000000000000000000000", + "data": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122", + "tags128": "702d595e74962d073a0d68c883d80deb41ab207e43b16659d556d7467218a9ec113406e7cb56e0f6b63c95c88421dfee", + "tag128": "a51f9bc5beae60cce77f0dbc60761edd", + "tags256": "a46ebcd10939b42012a3f9b6147172af3b74aec5d0070e8d6a81498ccbcdb41ad57cd7a50fa8621dfea2e81cd941def557094251a24527a4d97fc4c8253681803973129d07cc20811a8b3c34574f6ce010165dd0e856e797f70731e78e32f764", + "tag256": "b36a16ef07c36d75a91f437502f24f545b8dfa88648ed116943c29fead3bf10c" + } +] \ No newline at end of file diff --git a/tests/test-vectors/aesround-test-vector.json b/tests/test-vectors/aesround-test-vector.json new file mode 100644 index 0000000..efcc406 --- /dev/null +++ b/tests/test-vectors/aesround-test-vector.json @@ -0,0 +1,8 @@ +[ + { + "name": "AESRound Test Vector", + "in": "000102030405060708090a0b0c0d0e0f", + "rk": "101112131415161718191a1b1c1d1e1f", + "out": "7a7b4e5638782546a8c0477a3b813f43" + } +] \ No newline at end of file diff --git a/tests/test-vectors/gen.py b/tests/test-vectors/gen.py new file mode 100755 index 0000000..243e1f1 --- /dev/null +++ b/tests/test-vectors/gen.py @@ -0,0 +1,89 @@ +#! /usr/bin/env python3 + +import json +import re + + +def tvdump(topic, tvs): + with open(filename(topic), "w") as f: + f.write(json.dumps(tvs, indent=2)) + + print(json.dumps(tvs, indent=2)) + + +def filename(topic): + return re.sub(r"[^a-z0-9]+", "-", topic.lower()) + ".json" + + +header = True +in_tv = False +tv = {} +tvs = [] +must_fail = False +with open("../draft-irtf-cfrg-aegis-aead.md") as f: + for line in f: + line = line.strip() + if line == "": + continue + if line.startswith("# Test Vectors"): + header = False + continue + if header: + continue + + if line.startswith("## "): + if len(tvs) > 0: + tvdump(topic, tvs) + topic = line[3:] + tv_name = topic + tvs = [] + continue + + if line.startswith("### "): + tv_name = line[4:] + tv = {"test": tv_name} + in_tv = False + continue + + if line == "~~~ test-vectors": + in_tv = True + tv = {"name": tv_name} + if must_fail: + tv["error"] = "verification failed" + must_fail = False + continue + + if line == "~~~": + tvs.append(tv) + in_tv = False + current_key = None + continue + + if line.find("verification failed") != -1: + must_fail = True + continue + + if line == "After initialization:": + tv_name = tv_name + " (after initialization)" + + if not in_tv: + continue + + parts = line.split(":") + if len(parts) == 2: + key = parts[0].strip() + value = parts[1].strip() + if key == "After Update": + continue + if key in tv: + key = key + "_2" + tv[key] = value + current_key = key + continue + + if not current_key: + continue + + tv[key] += line.strip() + +tvdump(topic, tvs) diff --git a/tests/test_encrypt.py b/tests/test_encrypt.py new file mode 100644 index 0000000..3ebbda8 --- /dev/null +++ b/tests/test_encrypt.py @@ -0,0 +1,253 @@ +import json +from pathlib import Path +from sys import stderr + +import aegis.aegis128l +import aegis.aegis128x2 +import aegis.aegis128x4 +import aegis.aegis256 +import aegis.aegis256x2 +import aegis.aegis256x4 +import pytest + +from .util import random_split_bytes + + +def load_encryption_test_vectors(): + """Load encryption test vectors from all algorithm-specific JSON files.""" + test_vectors_path = Path(__file__).parent / "test-vectors" + vectors = [] + + # Map filename to algorithm module + algorithm_files = { + "aegis-128l-test-vectors.json": aegis.aegis128l, + "aegis-128x2-test-vectors.json": aegis.aegis128x2, + "aegis-128x4-test-vectors.json": aegis.aegis128x4, + "aegis-256-test-vectors.json": aegis.aegis256, + "aegis-256x2-test-vectors.json": aegis.aegis256x2, + "aegis-256x4-test-vectors.json": aegis.aegis256x4, + } + + for filename, alg_module in algorithm_files.items(): + filepath = test_vectors_path / filename + with open(filepath, "r") as f: + file_vectors = json.load(f) + # Filter and add algorithm info to testable vectors + for vector in file_vectors: + # Only include vectors that can be tested via the Python API + if ( + "key" in vector + and "nonce" in vector + and ("msg" in vector or "error" in vector) + ): + vector["_algorithm"] = alg_module + vectors.append(vector) + + return vectors + + +def get_encryption_test_id(vector): + """Generate a test ID from the vector name and algorithm.""" + alg_name = vector["_algorithm"].__name__.split(".")[ + -1 + ] # e.g., "aegis256" -> "aegis256" + vector_name = vector.get("name", "Unknown") + return f"{alg_name}-{vector_name}" + + +@pytest.mark.parametrize( + "vector", load_encryption_test_vectors(), ids=get_encryption_test_id +) +def test_encrypt_decrypt(vector): + """Test encryption and decryption against test vectors.""" + alg = vector["_algorithm"] + + key = bytes.fromhex(vector["key"]) + nonce = bytes.fromhex(vector["nonce"]) + ad = bytes.fromhex(vector.get("ad", "")) + + if "msg" in vector: + # Test encryption/decryption with valid message + msg = bytes.fromhex(vector["msg"]) + + # Test 128-bit MAC if present + if "tag128" in vector: + expected_tag128 = bytes.fromhex(vector["tag128"]) + ct, mac = alg.encrypt_detached(key, nonce, msg, ad, maclen=16) + + # Verify MAC + assert bytes(mac) == expected_tag128, ( + f"128-bit MAC mismatch for {vector['name']}" + ) + + # Verify ciphertext if present + if "ct" in vector: + expected_ct = bytes.fromhex(vector["ct"]) + assert bytes(ct) == expected_ct, ( + f"Ciphertext mismatch for {vector['name']}" + ) + + # Test successful decryption + decrypted = alg.decrypt_detached(key, nonce, ct, expected_tag128, ad) + assert bytes(decrypted) == msg, ( + f"Decryption failed for 128-bit MAC in {vector['name']}" + ) + + # Test 256-bit MAC if present + if "tag256" in vector: + expected_tag256 = bytes.fromhex(vector["tag256"]) + ct, mac = alg.encrypt_detached(key, nonce, msg, ad, maclen=32) + + # Verify MAC + assert bytes(mac) == expected_tag256, ( + f"256-bit MAC mismatch for {vector['name']}" + ) + + # Verify ciphertext if present + if "ct" in vector: + expected_ct = bytes.fromhex(vector["ct"]) + assert bytes(ct) == expected_ct, ( + f"Ciphertext mismatch for {vector['name']}" + ) + + # Test successful decryption + decrypted = alg.decrypt_detached(key, nonce, ct, expected_tag256, ad) + assert bytes(decrypted) == msg, ( + f"Decryption failed for 256-bit MAC in {vector['name']}" + ) + + elif "error" in vector: + # Test decryption failure cases + ct = bytes.fromhex(vector["ct"]) + + # Test that decryption fails with the provided (invalid) MACs + if "tag128" in vector: + invalid_mac = bytes.fromhex(vector["tag128"]) + with pytest.raises(ValueError, match="authentication failed"): + alg.decrypt_detached(key, nonce, ct, invalid_mac, ad) + + if "tag256" in vector: + invalid_mac = bytes.fromhex(vector["tag256"]) + with pytest.raises(ValueError, match="authentication failed"): + alg.decrypt_detached(key, nonce, ct, invalid_mac, ad) + + +@pytest.mark.parametrize( + "vector", load_encryption_test_vectors(), ids=get_encryption_test_id +) +def test_encrypt_decrypt_incremental(vector): + """Test incremental encryption and decryption using Encryptor/Decryptor classes.""" + alg = vector["_algorithm"] + + key = bytes.fromhex(vector["key"]) + nonce = bytes.fromhex(vector["nonce"]) + ad = bytes.fromhex(vector.get("ad", "")) + + if "msg" in vector: + # Test incremental encryption/decryption with valid message + msg = bytes.fromhex(vector["msg"]) + + # Test 128-bit MAC if present + if "tag128" in vector: + expected_tag128 = bytes.fromhex(vector["tag128"]) + + # Incremental encryption with random chunking + encryptor = alg.Encryptor(key, nonce, ad) + ct_chunks = [] + for chunk in random_split_bytes(msg): + ct_result = encryptor.update(chunk) + ct_chunks.append(bytes(ct_result)) + final_output = encryptor.final(maclen=16) + ct_chunks.append(bytes(final_output[:-16])) # ciphertext part + computed_mac = bytes(final_output[-16:]) # MAC part + + # Combine ciphertext chunks + computed_ct = b"".join(ct_chunks) + + # Verify against expected values + assert bytes(computed_mac) == expected_tag128, ( + f"128-bit MAC mismatch for {vector['name']}" + ) + if "ct" in vector: + expected_ct = bytes.fromhex(vector["ct"]) + assert computed_ct == expected_ct, ( + f"Ciphertext mismatch for {vector['name']}" + ) + + # Incremental decryption with different random chunking + decryptor = alg.Decryptor(key, nonce, ad) + pt_chunks = [] + stderr.write(f"CT len={len(computed_ct)}\n") + for chunk in random_split_bytes(computed_ct): + stderr.write( + f"Processing chunk of size {len(chunk)} {decryptor.bytes_in=}, out {alg.calc_update_output_size(decryptor.bytes_in, len(chunk))}\n" + ) + pt_chunks.append(bytes(decryptor.update(chunk))) + final_pt = decryptor.final(expected_tag128) + pt_chunks.append(bytes(final_pt)) + + # Combine plaintext chunks + computed_pt = b"".join(pt_chunks) + assert computed_pt == msg, ( + f"Decryption failed for 128-bit MAC in {vector['name']}" + ) + + # Test 256-bit MAC if present + if "tag256" in vector: + expected_tag256 = bytes.fromhex(vector["tag256"]) + + # Incremental encryption with random chunking + encryptor = alg.Encryptor(key, nonce, ad) + ct_chunks = [] + for chunk in random_split_bytes(msg): + ct_result = encryptor.update(chunk) + ct_chunks.append(bytes(ct_result)) + final_output = encryptor.final(maclen=32) + ct_chunks.append(bytes(final_output[:-32])) # ciphertext part + computed_mac = bytes(final_output[-32:]) # MAC part + + # Combine ciphertext chunks + computed_ct = b"".join(ct_chunks) + + # Verify against expected values + assert bytes(computed_mac) == expected_tag256, ( + f"256-bit MAC mismatch for {vector['name']}" + ) + if "ct" in vector: + expected_ct = bytes.fromhex(vector["ct"]) + assert computed_ct == expected_ct, ( + f"Ciphertext mismatch for {vector['name']}" + ) + + # Incremental decryption with different random chunking + decryptor = alg.Decryptor(key, nonce, ad) + pt_chunks = [] + for chunk in random_split_bytes(computed_ct): + pt_chunks.append(bytes(decryptor.update(chunk))) + final_pt = decryptor.final(expected_tag256) + pt_chunks.append(bytes(final_pt)) + + # Combine plaintext chunks + computed_pt = b"".join(pt_chunks) + assert computed_pt == msg, ( + f"Decryption failed for 256-bit MAC in {vector['name']}" + ) + + elif "error" in vector: + # Test decryption failure cases with incremental API + ct = bytes.fromhex(vector["ct"]) + + # Test that incremental decryption fails with the provided (invalid) MACs + if "tag128" in vector: + invalid_mac = bytes.fromhex(vector["tag128"]) + decryptor = alg.Decryptor(key, nonce, ad) + decryptor.update(ct) # This should succeed + with pytest.raises(ValueError, match="authentication failed"): + decryptor.final(invalid_mac) + + if "tag256" in vector: + invalid_mac = bytes.fromhex(vector["tag256"]) + decryptor = alg.Decryptor(key, nonce, ad) + decryptor.update(ct) # This should succeed + with pytest.raises(ValueError, match="authentication failed"): + decryptor.final(invalid_mac) diff --git a/tests/test_mac.py b/tests/test_mac.py new file mode 100644 index 0000000..2c956c2 --- /dev/null +++ b/tests/test_mac.py @@ -0,0 +1,106 @@ +import json +from pathlib import Path + +import aegis.aegis128l +import aegis.aegis128x2 +import aegis.aegis128x4 +import aegis.aegis256 +import aegis.aegis256x2 +import aegis.aegis256x4 +import pytest + +from .util import random_split_bytes + + +def load_mac_test_vectors(): + """Load MAC test vectors from JSON file.""" + test_vectors_path = ( + Path(__file__).parent / "test-vectors" / "aegismac-test-vectors.json" + ) + with open(test_vectors_path, "r") as f: + return json.load(f) + + +def get_algorithm_module(name): + """Map test vector name to algorithm module.""" + if "128L" in name: + return aegis.aegis128l + elif "128X2" in name: + return aegis.aegis128x2 + elif "128X4" in name: + return aegis.aegis128x4 + elif "256" in name and "256X2" not in name and "256X4" not in name: + return aegis.aegis256 + elif "256X2" in name: + return aegis.aegis256x2 + elif "256X4" in name: + return aegis.aegis256x4 + else: + raise ValueError(f"Unknown algorithm in test vector name: {name}") + + +def get_test_id(vector): + """Generate a test ID from the vector name.""" + name = vector["name"] + # Extract algorithm name, e.g., "AEGISMAC-128L Test Vector" -> "128L" + if "AEGISMAC-" in name: + return name.split("AEGISMAC-")[1].split(" ")[0] + return name + + +@pytest.mark.parametrize("vector", load_mac_test_vectors(), ids=get_test_id) +def test_mac(vector): + """Test MAC computation against test vectors.""" + alg = get_algorithm_module(vector["name"]) + + key = bytes.fromhex(vector["key"]) + nonce = bytes.fromhex(vector["nonce"]) + data = bytes.fromhex(vector["data"]) + + # Test 128-bit MAC if present + if "tag128" in vector: + expected_tag128 = bytes.fromhex(vector["tag128"]) + computed_tag128 = alg.mac(key, nonce, data, maclen=16) + assert computed_tag128 == expected_tag128, ( + f"128-bit MAC mismatch for {vector['name']}" + ) + + # Test 256-bit MAC if present + if "tag256" in vector: + expected_tag256 = bytes.fromhex(vector["tag256"]) + computed_tag256 = alg.mac(key, nonce, data, maclen=32) + assert computed_tag256 == expected_tag256, ( + f"256-bit MAC mismatch for {vector['name']}" + ) + + +@pytest.mark.parametrize("vector", load_mac_test_vectors(), ids=get_test_id) +def test_mac_class(vector): + """Test MAC computation using the Mac class against test vectors.""" + alg = get_algorithm_module(vector["name"]) + + key = bytes.fromhex(vector["key"]) + nonce = bytes.fromhex(vector["nonce"]) + data = bytes.fromhex(vector["data"]) + + # Test 128-bit MAC if present + if "tag128" in vector: + expected_tag128 = bytes.fromhex(vector["tag128"]) + mac_state = alg.Mac(key, nonce) + for chunk in random_split_bytes(data): + mac_state.update(chunk) + computed_tag128 = mac_state.final(maclen=16) + assert computed_tag128 == expected_tag128, ( + f"128-bit MAC mismatch for {vector['name']}" + ) + + # Test 256-bit MAC if present + if "tag256" in vector: + expected_tag256 = bytes.fromhex(vector["tag256"]) + mac_state = alg.Mac(key, nonce) + for chunk in random_split_bytes(data): + mac_state.update(chunk) + computed_tag256 = mac_state.final(maclen=32) + assert computed_tag256 == expected_tag256, ( + f"256-bit MAC mismatch for {vector['name']}" + ) diff --git a/tests/util.py b/tests/util.py new file mode 100644 index 0000000..f72e626 --- /dev/null +++ b/tests/util.py @@ -0,0 +1,28 @@ +import random + + +_rng = random.SystemRandom() + + +def random_split_bytes(data: bytes) -> list[bytes]: + """Split ``data`` into one to a few random-sized chunks. + + Keeps chunk sizes positive and varies the number of pieces between 1 and 4. + """ + if not data: + return [data] + if len(data) == 1: + return [data] + + max_chunks = min(4, len(data)) + chunks = _rng.randint(1, max_chunks) + if chunks == 1: + return [data] + + cut_points = sorted(_rng.sample(range(1, len(data)), chunks - 1)) + start = 0 + parts = [] + for stop in (*cut_points, len(data)): + parts.append(data[start:stop]) + start = stop + return parts \ No newline at end of file