commit 825f837e0edf16ea44faaada8e7043b7a726c371 Author: Aaron Kaiser Date: Sat Feb 17 11:34:17 2024 +0100 MVP diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c87b82f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,976 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "age-core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5f11899bc2bbddd135edbc30c36b1924fa59d0746bb45beb5933fafe3fe509b" +dependencies = [ + "base64", + "chacha20poly1305", + "cookie-factory", + "hkdf", + "io_tee", + "nom", + "rand", + "secrecy", + "sha2", + "tempfile", +] + +[[package]] +name = "age-plugin" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04740d993aac0e06eaf4cfbf8484a8a23a6a7f950cf5d53bdf2d6ea3f429eb9d" +dependencies = [ + "age-core", + "base64", + "bech32", + "chrono", +] + +[[package]] +name = "age-plugin-xwing" +version = "0.1.0" +dependencies = [ + "age-core", + "age-plugin", + "base64", + "bech32", + "chrono", + "clap", + "xwing-kem", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "chrono" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clap" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "cookie-factory" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fiat-crypto" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "io_tee" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b3f7cef34251886990511df1c61443aa928499d598a9473929ab5a90a527304" + +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "platforms" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "pqcrypto-internals" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9d34bec6abe2283e6de7748b68b292d1ffa2203397e3e71380ff8418a49fb46" +dependencies = [ + "cc", + "dunce", + "getrandom", + "libc", +] + +[[package]] +name = "pqcrypto-kyber" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c00293cf898859d0c771455388054fd69ab712263c73fdc7f287a39b1ba000" +dependencies = [ + "cc", + "glob", + "libc", + "pqcrypto-internals", + "pqcrypto-traits", +] + +[[package]] +name = "pqcrypto-traits" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e851c7654eed9e68d7d27164c454961a616cf8c203d500607ef22c737b51bb" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "xwing-kem" +version = "0.1.0" +dependencies = [ + "arrayref", + "pqcrypto-kyber", + "pqcrypto-traits", + "sha3", + "x25519-dalek", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5afb744 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "age-plugin-xwing" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +age-core = "0.10.0" +age-plugin = "0.5.0" +base64 = "0.21.7" +bech32 = "0.9.1" +chrono = "0.4.34" +clap = { version = "4.5.1", features = ["derive"] } +xwing-kem = { path = "xwing-kem.rs" } diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ec27f94 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,167 @@ +use age_core::format::{FileKey, Stanza}; +use age_core::primitives::{aead_encrypt, aead_decrypt}; +use age_core::secrecy::ExposeSecret; +use age_plugin::{ + identity::{self, IdentityPluginV1}, + recipient::{self, RecipientPluginV1}, + Callbacks, run_state_machine, +}; +use bech32::ToBase32; +use clap::Parser; +use xwing_kem::{XwingPublicKey, XwingSecretKey, XwingCiphertext}; +use base64::prelude::*; + +use std::collections::HashMap; +use std::io; + +const RECIPIENT_PREFIX: &str = "age1xwing"; +const IDENTITY_PREFIX: &str = "AGE-PLUGIN-XWING-"; +const STANZA_TAG: &str = "xwing"; + +#[derive(Default)] +struct RecipientPlugin { + recipients: Vec +} + +impl RecipientPluginV1 for RecipientPlugin { + fn add_recipient( + &mut self, + index: usize, + _plugin_name: &str, + bytes: &[u8], + ) -> Result<(), recipient::Error> { + let bytes = match bytes.try_into() { + Ok(x) => x, + _ => return Err(recipient::Error::Recipient { index, message: "Invalid recipient".to_owned() }) + }; + self.recipients.push(XwingPublicKey::from(bytes)); + Ok(()) + } + + fn add_identity( + &mut self, + _index: usize, + _plugin_name: &str, + _bytes: &[u8] + ) -> Result<(), recipient::Error> { + unimplemented!() + } + + fn wrap_file_keys( + &mut self, + file_keys: Vec, + mut _callbacks: impl Callbacks, + ) -> io::Result>, Vec>> { + Ok(Ok(file_keys + .into_iter() + .map(|file_key| { + self.recipients + .iter() + .map(|recipient| { + let (ss, ct) = recipient.encapsulate(); + let wrapped_key = aead_encrypt(&ss.to_bytes(), file_key.expose_secret()); + Stanza { tag: STANZA_TAG.to_string(), args: vec![BASE64_STANDARD.encode(ct.to_bytes())], body: wrapped_key } + }) + .collect() + }) + .collect())) + } +} + +#[derive(Default)] +struct IdentityPlugin { + identities: Vec +} + +impl IdentityPluginV1 for IdentityPlugin { + fn add_identity( + &mut self, + index: usize, + _plugin_name: &str, + bytes: &[u8] + ) -> Result<(), identity::Error> { + let bytes = match bytes.try_into() { + Ok(x) => x, + _ => return Err(identity::Error::Identity { index, message: "Invalid identity".to_owned() }) + }; + self.identities.push(XwingSecretKey::from(bytes)); + Ok(()) + } + + fn unwrap_file_keys( + &mut self, + files: Vec>, + mut _callbacks: impl Callbacks, + ) -> io::Result>>> { + Ok(files.iter().enumerate().map(|(file_index, stanzas)| { + let result: Vec> = stanzas.into_iter().enumerate().map(|(stanza_index, stanza)| { + let decryptions: Vec> = self.identities.iter().map(|identity| { + let ct = match BASE64_STANDARD.decode(&stanza.args.get(0).unwrap_or(&"".to_string())).unwrap_or_default().try_into() { + Ok(x) => x, + _ => return None + }; + let ss = identity.decapsulate(XwingCiphertext::from(ct)); + + match aead_decrypt(&ss.to_bytes(), 16, &stanza.body) { + Ok(file_key) => { + let file_key: [u8; 16] = file_key.try_into().expect("This should never fail"); + Some(file_key) + }, + _ => None + } + }).collect(); + + let file_key = decryptions.into_iter().filter(|file_key| file_key.is_some()).map(|file_key| file_key.expect("This should never fail")).next(); + if let Some(file_key) = file_key { + return Ok(file_key) + } + + Err(identity::Error::Stanza { file_index, stanza_index, message: "Invalid stanza".to_owned() }) + }).collect(); + + let file_key = result.iter().filter(|file_key| file_key.is_ok()).map(|file_key| file_key.as_ref().ok().expect("This should never fail")).next(); + if let Some(file_key) = file_key { + return (file_index, Ok(FileKey::from(file_key.to_owned()))); + } + + (file_index, Err(result.into_iter().map(|identity_error| identity_error.err().expect("This should never fail")).collect())) + }).collect()) + } +} + +#[derive(Debug, Parser)] +struct PluginOptions { + #[arg(help = "run the given age plugin state machine", long)] + age_plugin: Option, +} + +fn main() -> io::Result<()> { + let opts = PluginOptions::parse(); + + if let Some(state_machine) = opts.age_plugin { + // The plugin was started by an age client; run the state machine. + run_state_machine( + &state_machine, + Some(|| RecipientPlugin::default()), + Some(|| IdentityPlugin::default()), + )?; + return Ok(()); + } + + // Here you can assume the binary is being run directly by a user, + // and perform administrative tasks like generating keys. + let (sk, pk) = xwing_kem::generate_keypair(); + println!( + "# created: {}", + chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true) + ); + println!( + "# public key: {}", + bech32::encode(RECIPIENT_PREFIX, pk.to_vec().to_base32(), bech32::Variant::Bech32).unwrap().to_lowercase().as_str() + + ); + println!("{}", bech32::encode(IDENTITY_PREFIX, sk.to_vec().to_base32(), bech32::Variant::Bech32).unwrap().to_ascii_uppercase().as_str()); + + + Ok(()) +} diff --git a/xwing-kem.rs/.gitignore b/xwing-kem.rs/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/xwing-kem.rs/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/xwing-kem.rs/Cargo.toml b/xwing-kem.rs/Cargo.toml new file mode 100644 index 0000000..8202e0b --- /dev/null +++ b/xwing-kem.rs/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "xwing-kem" +version = "0.1.0" +edition = "2021" +readme = "README.md" +license = "BSD-3-Clause" +description = "Xwing hybrid combiner KEM utilizing MLKEM/Kyber and X25519. See https://eprint.iacr.org/2024/039." +homepage = "https://github.com/rugo/xwing-kem.rs" +documentation = "https://github.com/rugo/xwing-kem.rs" +repository = "https://github.com/rugo/xwing-kem.rs" +keywords = ["cryptography", "kyber", "post-quantum", "hybrid", "curve25519"] +categories = ["cryptography", "post-quantum"] +authors = [ + "Ruben Gonzalez ", +] + +[dependencies] +pqcrypto-kyber = "0.8.0" +pqcrypto-traits = "0.3.5" +x25519-dalek = { version = "2", features = ["getrandom", "static_secrets"] } +sha3 = "0.10.8" +arrayref = "0.3" + +[dev-dependencies] +hex = "0.4.3" diff --git a/xwing-kem.rs/LICENSE b/xwing-kem.rs/LICENSE new file mode 100644 index 0000000..801bb97 --- /dev/null +++ b/xwing-kem.rs/LICENSE @@ -0,0 +1,28 @@ +Copyright 2023 Ruben Gonzalez. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/xwing-kem.rs/README.md b/xwing-kem.rs/README.md new file mode 100644 index 0000000..a73ac0c --- /dev/null +++ b/xwing-kem.rs/README.md @@ -0,0 +1,59 @@ +# Xwing KEM for Rust + +This is a Rust implementation of the hybrid Xwing KEM using Kyber768 (post-quantum) and x25519 (pre-quantum). +For primitives it uses a [wrapper](https://github.com/rustpq/pqcrypto) around [PQClean](https://github.com/pqclean/PQClean/tree/0657749a785db30e7f49e9435452cb042edb1852) and [x25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek/tree/main/x25519-dalek). + +The details of Xwing are specified in the: + +* [Paper](https://eprint.iacr.org/2024/039) +* [IETF Draft](https://datatracker.ietf.org/doc/draft-connolly-cfrg-xwing-kem/) + +## Usage +The lib exposes functions for use with buffers and some wrapper structs. + +Example usage: + +```rust +use xwing_kem::{XwingKeyPair, XwingCiphertext}; + +fn main() { + // Using buffers + println!("Computing Keypair!"); + let (sk, pk) = xwing_kem::generate_keypair(); + + println!("Encapsulating secret to be transmitted!"); + let (shared_secret, ciphertext) = xwing_kem::encapsulate(pk); + + println!("Decapsulating ciphertext with the secret key to get shared secret!"); + let computed_shared_secret = xwing_kem::decapsulate(ciphertext, sk); + + // Using structs + println!("Computing Keypair!"); + let keypair = XwingKeyPair::generate(); + + println!("Encapsulating secret to be transmitted!"); + let (ss, ct) = keypair.pk.encapsulate(); + + println!("Serializing ciphertext to be transmitted!"); + let ct_bytes = ct.to_bytes(); + + println!("Deserializing ciphertext!"); + let ct_res = XwingCiphertext::from(ct_bytes); + + println!("Decapsulating ciphertext with the secret key to get shared secret!"); + let ss_result = keypair.sk.decapsulate(ct_res); + + assert_eq!(ss, ss_result); + + println!("Shared secret is: {:x?}", ss_result) +} +``` + +## Examples +Two examples are included, [alice](examples/alice.rs) uses Xwing directly with buffers, [bob](examples/bob.rs) uses wrapper structs. + +To run an example call: + +```sh +cargo run --example bob +``` \ No newline at end of file diff --git a/xwing-kem.rs/examples/alice.rs b/xwing-kem.rs/examples/alice.rs new file mode 100644 index 0000000..870cef4 --- /dev/null +++ b/xwing-kem.rs/examples/alice.rs @@ -0,0 +1,14 @@ +fn main() { + println!("Computing Keypair!"); + let (sk, pk) = xwing_kem::generate_keypair(); + + println!("Encapsulating secret to be transmitted!"); + let (shared_secret, ciphertext) = xwing_kem::encapsulate(pk); + + println!("Decapsulating ciphertext with the secret key to get shared secret!"); + let computed_shared_secret = xwing_kem::decapsulate(ciphertext, sk); + + assert_eq!(shared_secret, computed_shared_secret); + + println!("Shared secret is: {:x?}", computed_shared_secret) +} \ No newline at end of file diff --git a/xwing-kem.rs/examples/bob.rs b/xwing-kem.rs/examples/bob.rs new file mode 100644 index 0000000..e18c846 --- /dev/null +++ b/xwing-kem.rs/examples/bob.rs @@ -0,0 +1,22 @@ +use xwing_kem::{XwingKeyPair, XwingCiphertext}; + +fn main() { + println!("Computing Keypair!"); + let keypair = XwingKeyPair::generate(); + + println!("Encapsulating secret to be transmitted!"); + let (ss, ct) = keypair.pk.encapsulate(); + + println!("Serializing ciphertext to be transmitted!"); + let ct_bytes = ct.to_bytes(); + + println!("Deserializing ciphertext!"); + let ct_res = XwingCiphertext::from(ct_bytes); + + println!("Decapsulating ciphertext with the secret key to get shared secret!"); + let ss_result = keypair.sk.decapsulate(ct_res); + + assert_eq!(ss, ss_result); + + println!("Shared secret is: {:x?}", ss_result) +} \ No newline at end of file diff --git a/xwing-kem.rs/src/lib.rs b/xwing-kem.rs/src/lib.rs new file mode 100644 index 0000000..f8d171e --- /dev/null +++ b/xwing-kem.rs/src/lib.rs @@ -0,0 +1,3 @@ +mod xwing; + +pub use crate::xwing::*; \ No newline at end of file diff --git a/xwing-kem.rs/src/xwing.rs b/xwing-kem.rs/src/xwing.rs new file mode 100644 index 0000000..9381028 --- /dev/null +++ b/xwing-kem.rs/src/xwing.rs @@ -0,0 +1,313 @@ +use x25519_dalek::{StaticSecret as X25519SecretKey, EphemeralSecret as X25519EphemeralSecret, PublicKey as X25519PublicKey}; +use pqcrypto_kyber::{kyber768, ffi::{PQCLEAN_KYBER768_CLEAN_CRYPTO_SECRETKEYBYTES, PQCLEAN_KYBER768_CLEAN_CRYPTO_PUBLICKEYBYTES, PQCLEAN_KYBER768_CLEAN_CRYPTO_CIPHERTEXTBYTES, PQCLEAN_KYBER768_CLEAN_CRYPTO_BYTES}}; +use pqcrypto_traits::kem::{SecretKey as KemSecretKey, PublicKey as KemPublicKey, SharedSecret as KemSharedSecret, Ciphertext as KemCiphertext}; +use sha3::{Digest, Sha3_256}; +use arrayref::array_ref; + + +const XWING_LABEL: &[u8] = b"\\.//^\\"; +const KYBER_SK_BYTES: usize = PQCLEAN_KYBER768_CLEAN_CRYPTO_SECRETKEYBYTES; +const KYBER_PK_BYTES: usize = PQCLEAN_KYBER768_CLEAN_CRYPTO_PUBLICKEYBYTES; +const KYBER_CT_BYTES: usize = PQCLEAN_KYBER768_CLEAN_CRYPTO_CIPHERTEXTBYTES; +const KYBER_SS_BYTES: usize = PQCLEAN_KYBER768_CLEAN_CRYPTO_BYTES; + + +const X25519_SK_BYTES: usize = 32; +const X25519_PK_BYTES: usize = 32; +const X25519_CT_BYTES: usize = 32; +const X25519_SS_BYTES: usize = 32; + +const XWING_SK_BYTES: usize = KYBER_SK_BYTES + X25519_SK_BYTES; +const XWING_PK_BYTES: usize = KYBER_PK_BYTES + X25519_PK_BYTES; +const XWING_CT_BYTES: usize = KYBER_CT_BYTES + X25519_CT_BYTES; +const XWING_SS_BYTES: usize = 32; // Sha3_256 output size + + +pub fn generate_keypair() -> ([u8; XWING_SK_BYTES], [u8; XWING_PK_BYTES]) { + + let (pk_kyber, sk_kyber) = kyber768::keypair(); + + let sk_x25519 = X25519SecretKey::random(); + let pk_x25519 = X25519PublicKey::from(&sk_x25519); + + let mut xwing_sk= [0u8; XWING_SK_BYTES]; + let mut xwing_pk = [0u8; XWING_PK_BYTES]; + + xwing_sk[..KYBER_SK_BYTES].copy_from_slice(sk_kyber.as_bytes()); + xwing_sk[KYBER_SK_BYTES..].copy_from_slice(sk_x25519.as_bytes()); + + xwing_pk[..KYBER_PK_BYTES].copy_from_slice(pk_kyber.as_bytes()); + xwing_pk[KYBER_PK_BYTES..].copy_from_slice(pk_x25519.as_bytes()); + + return (xwing_sk, xwing_pk) +} + + +fn combiner(ss_kyber: &[u8; KYBER_SS_BYTES], ss_x25519: &[u8; X25519_SS_BYTES], ct_x25519: &[u8; X25519_CT_BYTES], pk_x25519: &[u8; X25519_PK_BYTES]) -> [u8; 32] { + let mut hasher = Sha3_256::new(); + + hasher.update(XWING_LABEL); + hasher.update(ss_kyber); + hasher.update(ss_x25519); + hasher.update(ct_x25519); + hasher.update(pk_x25519); + + hasher.finalize().try_into().unwrap() +} + + +pub fn encapsulate(pk: [u8; XWING_PK_BYTES]) -> ([u8; XWING_SS_BYTES], [u8; XWING_CT_BYTES]) { + let pk_kyber = &pk[..KYBER_PK_BYTES]; + let pk_kyber = KemPublicKey::from_bytes(pk_kyber).unwrap(); + + let pk_x25519 = *array_ref![&pk[KYBER_PK_BYTES..], 0, X25519_PK_BYTES]; + + + let tmp_x25519 = X25519EphemeralSecret::random(); + let ct_x25519 = X25519PublicKey::from(&tmp_x25519).to_bytes(); + let ss_x25519 = tmp_x25519.diffie_hellman(&X25519PublicKey::from(pk_x25519)); + + let (ss_kyber, ct_kyber) = kyber768::encapsulate(&pk_kyber); + + let xwing_ss = combiner( + ss_kyber.as_bytes().try_into().unwrap(), + ss_x25519.as_bytes(), + &ct_x25519, + &pk_x25519 + ); + + let mut xwing_ct = [0u8; XWING_CT_BYTES]; + xwing_ct[..KYBER_CT_BYTES].copy_from_slice(ct_kyber.as_bytes()); + xwing_ct[KYBER_CT_BYTES..].copy_from_slice(&ct_x25519); + + (xwing_ss, xwing_ct) +} + + +pub fn decapsulate(ct: [u8; XWING_CT_BYTES], sk: [u8; XWING_SK_BYTES]) -> [u8; 32] { + let ct_kyber = &ct[..KYBER_CT_BYTES]; + let ct_x25519 = *array_ref![&ct[KYBER_CT_BYTES..], 0, X25519_CT_BYTES]; + + let sk_kyber = &sk[..KYBER_SK_BYTES]; + let sk_x25519 = &sk[KYBER_SK_BYTES..]; + + let pk_x25519 = X25519PublicKey::from( + &X25519SecretKey::from(*array_ref![sk_x25519, 0, X25519_SK_BYTES]) + ).to_bytes(); + + let ss_kyber = kyber768::decapsulate( + &KemCiphertext::from_bytes(ct_kyber).unwrap(), + &KemSecretKey::from_bytes(sk_kyber).unwrap() + ); + + + let sk_x25519 = X25519SecretKey::from(*array_ref![sk_x25519, 0, X25519_SK_BYTES]); + let ss_x25519 = sk_x25519.diffie_hellman(&X25519PublicKey::from(ct_x25519)); + + combiner( + ss_kyber.as_bytes().try_into().unwrap(), + ss_x25519.as_bytes(), + &ct_x25519, + &pk_x25519 + ) +} + + +#[derive(PartialEq, Eq)] +pub struct XwingSecretKey([u8; XWING_SK_BYTES]); + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct XwingPublicKey([u8; XWING_PK_BYTES]); + +#[derive(PartialEq, Eq, Debug)] +pub struct XwingSharedSecret([u8; XWING_SS_BYTES]); + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct XwingCiphertext([u8; XWING_CT_BYTES]); + +pub struct XwingKeyPair { + pub sk: XwingSecretKey, + pub pk: XwingPublicKey +} + +impl XwingSecretKey { + pub fn decapsulate(&self, ct: XwingCiphertext) -> XwingSharedSecret { + XwingSharedSecret(decapsulate(ct.0, self.0)) + } + + pub fn from(bytes: [u8; XWING_SK_BYTES]) -> XwingSecretKey { + XwingSecretKey(bytes) + } +} + +impl XwingPublicKey { + pub fn encapsulate(&self) -> (XwingSharedSecret, XwingCiphertext) { + let (ss, ct) = encapsulate(self.0); + (XwingSharedSecret(ss), XwingCiphertext(ct)) + } + + pub fn to_bytes(self) -> [u8; XWING_PK_BYTES] { + self.0 + } + + pub fn from(bytes: [u8; XWING_PK_BYTES]) -> XwingPublicKey { + XwingPublicKey(bytes) + } +} + +impl XwingSharedSecret { + pub fn to_bytes(&self) -> [u8; XWING_SS_BYTES] { + self.0 + } +} + +impl XwingCiphertext { + pub fn to_bytes(&self) -> [u8; XWING_CT_BYTES] { + self.0 + } + + pub fn from(bytes: [u8; XWING_CT_BYTES]) -> XwingCiphertext { + XwingCiphertext(bytes) + } +} + +impl XwingKeyPair { + pub fn generate() -> XwingKeyPair { + let (sk, pk) = generate_keypair(); + + XwingKeyPair{ + sk: XwingSecretKey(sk), + pk: XwingPublicKey(pk) + } + } +} + + +#[cfg(test)] +mod tests { + use super::*; + use hex; + + #[test] + fn test_combiner() { + let ss_m = [0x37u8; 32]; + let ss_x = [0x38u8; 32]; + let ct_x = [0x39u8; 32]; + let pk_x = [0x40u8; 32]; + + let result = [228, 196, 54, 163, 34, 255, 199, 110, 124, 74, 193, 167, 67, 59, 134, 255, 182, 126, 43, 213, 94, 93, 232, 173, 142, 104, 254, 109, 31, 102, 225, 213]; + + assert_eq!(combiner(&ss_m, &ss_x, &ct_x, &pk_x), result); + } + + + #[test] + fn test_encaps_decaps() { + let (sk, pk) = generate_keypair(); + + let (ss, ct) = encapsulate(pk); + + let ss2 = decapsulate(ct, sk); + + assert_eq!(ss, ss2); + } + + #[test] + fn test_encaps_decaps_structs() { + let keypair = XwingKeyPair::generate(); + + let (ss, ct) = keypair.pk.encapsulate(); + + let computed_ss = keypair.sk.decapsulate(ct); + + assert_eq!(computed_ss.to_bytes(), ss.to_bytes()) + } + + #[test] + fn test_to_from() { + let keypair = XwingKeyPair::generate(); + + let pk = XwingPublicKey::from(keypair.pk.to_bytes()); + + let (ss, ct) = pk.encapsulate(); + + let ct = XwingCiphertext::from(ct.to_bytes()); + + let ss_result = keypair.sk.decapsulate(ct); + + assert_eq!(ss_result.to_bytes(), ss.to_bytes()) + } + + #[test] + fn test_vecs() { + let sk = hex::decode("24c59d1c7603e7b74bc7aa1bc2cb3a214b3cfaebb63bd85b65408427c498ba394371bb271f92a3b + 506b81d54a95a7c0ddfbaa1519553d6f3cd5a601b7db6b0e91a5149468f1f68ad26478bf3c6670e093ac4c49e7a90ba46595de94c50e04129a81 + 1a841b39534a87f0ae7b1116553e20c9a566b9b8ff7c7e728b8b201893403a4f252a55230874c256b897834cda349807b25cbd75a30867bfb803 + 28200017f1cb70b56cc546b65d3dc9cdb45107cf10dba349619043ac35c0b9546309a239039813ed5c40f353a5e8e42193564496112bda56cb38 + c081df252ae9c2c7e441a062e92a7c8da7a240c9952d86b5f1bb6a53b38a5ac0a54a84b43f12da1d0525655684a12090b60b28b0c628db092015 + 547d1070af5d6192e639636615d03c654bb90008ca15b784119f6178a00d7bef4a54a274ac922e55c61a3a8840aa258639484a3bce2e43b6c969 + b11275631daa129a61ea0e2939f0877e1a110c8a44b24c54fbb07a958db9feeca1eb52b086c87bf43a9b02a5b2c4762117c3a99ae4c4e2eaa7a3 + 3b9a714737215c10317514f6c4299ef92acd64c4858e85ce737a801890022d7381f3540230c0c8ef50a848a28b09ba0bf8b50619c905751601d7 + 629767449c9c0b2bae321f438a77f412a55e45ecab4b39053c6561801c639be6495be8fa144ef6029af663407ca9181946de5f3aec7236343ab3 + bc5a38a09c01b412baf0afb23f9e9b8f2b40810f2ce4ffbcdbfd87972323e98065160bcba34b3afd6c25b664745fca99a9ea75cef019d768485e + c23336d9b39e4d05d8d587b30633d4f69ade5753a39680235e44f27995da96798f3a85e184a9fad19320829629f4140417bb7dbf5851ab792581 + 34146d088452774991a087a1c2beaea89f218087ba774ae253b494c27750b1de04b44d953c5e47ab10f65205ee212f9c30391e52995539549168 + 73a0b41164543e801c0b099cb44f48995675823c10b40f4bbac9177a558ca0c30765c2aabfd6a4da54c8413e33902d63f064330f0464982429de + 2604cd03b4de84a9f821a5470423a40a964dcc41863363d77b02c3127304f942ee71c98c643a427533ef300104948b825277953aaabfd855588f + 75a77d199a213ad348116e9e539f6d37068a551c710548b7a2c7ee95f9cd9b3483332673cc44bcb18a778a49455c768e0b340f81102ac6b76b06 + 4057151ef101ae143787f548553558df8035a3ce00c9c43cda43142cca39034b09a7e6089867b4c64980a69ecab2e6818724c35cb909d5d45bc6 + a349c71b306567664adc0cc8ef698049b4b4b432dd0f69fac07580f77c4f79b22bb90cb97b341880716853431694c9120f6724ad58d57127fced + 999ff6229a5d4c3c240129cc812acc73698f949d8e73661f2528262bfccfa5cdf5a2104649806e295ea161217083365aa26cee6ae2f1356e8e1c + 5cefcc85703447ef1160a1b4a0e8c017b173802c66c88ab70d39a6c96c1569d5a86245a7eeb087d682219080768745b44bf244f65b567b2658db + ae6962ba52b322118e214cfadd7cf3502582dc9cafba952a9637ad3600710259778d99d23f8235da90791604b4f0a4f7640680f59b633d93dfb8 + 4282ba54c674b115684a41bc331b659a61a04883d0c5ebbc0772754a4c33b6a90e52e0678ce06a0453ba8a188b15a496bae6a24177b636d12fbb + 088f2cd9504ac200231473031a31a5c62e46288fb3edb858b21bc0ea59a212fd1c6dba09e920712d068a2be7abcf4f2a3533443ee1780dd41968 + 1a960cd90af5fcaab8c1552ef25572f157a2bbb934a18a5c57a761b54a45d774ac6bc593583a1bcfc4dcd0cca87ab9cff463dc5e80ebbb501d18 + c8b39e324dbd07ca06cbf75ba33297abcc7aabdd5b308401ba387f533f3927b51e91380f5a59b119e354835ab182db62c76d6d85fa63241743a5 + 2012aac281222bc0037e2c493b4777a99cb5929aba155a006bc9b461c365fa3583fac5414b403af9135079b33a10df8819cb462f067253f92b3c + 45a7fb1c1478d4091e39010ba44071019010daa15c0f43d14641a8fa3a94cfaa2a877ae8113bbf8221ee13223376494fb128b825952d5105ae41 + 57dd6d70f71d5bd48f34d469976629bce6c12931c88ca0882965e27538f272b19796b251226075b131b38564f90159583cd9c4c3c098c8f06a26 + 7b262b8731b9e962976c41152a76c30b502d0425635357b43cd3a3ecef5bc9910bb89ca9e91ba75e8121d53c2329b5222df12560d242724523ff + 60b6ead310d99954d483b91383a726a937f1b60b474b22ea5b81954580339d81c9f47bab44a3fe0c833a7dba1f5b33a5a2a459812645c6537c23 + 17163d71b7bd7a4a5459a28a1c28659aad9a1ca9a99a363062d453355108445a673438e77624e73757c1a84d031cf0fb24b1187aafbe6738e9ab + af5b42b004b1fa0d96426d3c5324235dd871e7a89364d335ebb6718ad098154208b143b2b43eb9e5fd8816c5225d494b40809b2459903c6486a1 + db9ac3414945e1867b5869c2f88cf9edc0a216681804578d34923e5a353babba923db907725b384e74e66987292e007e05c6766f267f839b7617 + c55e28b0fa2121da2d037d6830af9d869e1fb52b0cb645fe221a79b2a46e41980d34671ccc58d8756054b2cca7b13715a05f3925355cca838ab8 + d2425255f61135727167ad6bcb0632ebf86384b950ad21088c292b4a4fcc0e59c42d3f77fac85cd9f5cb049b3a29505a984c4c6ac98ca3d0a8f3 + 0d2b1bd9815b94b27051b40ffc3455a668b9e141428611b280c1b8f2b55f6eb04e10c68f1340ef1582115f10ee2b785b7ebb0ec3a0c61670cf48 + 107b594cd6e238e0d68961b47983b87879771519d2b7c21681cd494b420f03d004bb06eeb54f9c080c2f2aff6759074d5b3a3b11c73f1af6dc87 + 4eeec254d5409fceaa90ff66d90b6930a540fd1d9be1844af1d861ff96a611a414a6c61a78fb2a78e74383ab05ebc73855a818a627242d523a3e + 2a35ab4285b4a2564f76772aaf8cdc9f87c65f1b4b5819905fb4f9ea59166fbbdb201c5eefc0df7418ca211b5b079a511b8b94429847b537fbed + 82d57632d63e815d8212d8a280d43328604a6c4d2c1887e7ab061f120a0168db2f4735369b193780f0aeb381ff2653f3b46e206afe77a7e814c7 + 716a1b166727dd2a0b9a7d8aeace425da63977f8103457c9f438a2676c10e3a9c630b855873288ee560ca05c37cc7329e9e502cfac918b942054 + 4445d4cfa93f56ee922c7d660937b5937c3074d62968f006d1211c60296685953e5def3804c2dad5c36180137c1df12f31385b670fde5cfe7644 + 7f6c4b5b50083553c3cb1eea988004b93103cfb0aeefd2a686e01fa4a58e8a3639ca8a1e3f9ae57e235b8cc873c23dc62b8d260169afa2f75ab9 + 16a58d974918835d25e6a435085b2".replace("\n", "").replace(" ", "")).unwrap(); + let ct = hex::decode("718ad10318b367fc4390f63147fa5250ef61b65384a563f2c7951b2d45881fcf9f446ddd4443417eed0c001e635a994cda366f118bdd1cf0be04 + 17abd1b615cc669e1b949280e28f52d3d5035c6420ff6c943421ee7589e681828c95942d4f9968f32b9ad30cccff0d98fa84b187164530dc83f9 + cde75ab1958c22dbff8af921c9ebc678a658b69663f72e7c1632b6ac8ddcbc6c8a06c3316b1aefdd07989ef944fc51406e12db6865344e03f447 + 520d50c93fab1513d80cbc836950e2b52f424bb46155ba4c2e21ec5dff762bf7e92e54e0fb7618e73072607ba03b1de16f109e22dd5832a7eadf + eb2ef00244bbaf930106cbcd2ab008f468de6d98632e9e225091a010e361ce751d633e6c37ba2530bca6fbe9d2e5348e4e168e154922992aef45 + a265ec649ce21480504b609ad5f1b0b094b74d55aaea60b8f71398cd9340802e91415937ffaa482c6678f8421c63583e8acd8d00bf285b52a26f + a577aed109acd94ef7559554aa378f87283a7ee94af98e21a6fbac8802336ff980e15e498042a8148b69e1d8aab0b7126d0b885f9a57c1ea83ef + cce8dccfee076dbc2f9c074525ed4e7472c3e09a9f1c50ff511150159c1be7730686c04e46368e37f2e8c82b8436463445b0edaefab876731497 + abcc563b1978eac34cf73b5b213549d1f74271d48f6a085155acd8d7db739ce6e70ad25ee636231e4151725d55ea781d483e54850e1ebda40127 + 6616e7a62b22efa2e3098a006dfacaa1fca54ade6a119f3a215b523210164a7f299d2c7b8ad8a637bc1fba56de28ffa800b522246dbec7148ced + 56ed292c7d92004065598bc573dd30259d84b6d923d2769ce260cdab0ad17673ef7388c020b8e8bcd055232a7240fe2fa4fcbeadbc46366aa477 + 29f5502dbfee8a623ab8ec6f6020013aeff975f255b597a11eed1335457b9903da42a27a39fdb0edbb11742e4e521c833b7952d3fd28f428eecb + 6f78b99ff0a5eb097793f78f1a70612811766fcbe0f9aa3ca4afd8a364f5584333d8a4cdc096a3762ea6cce70dfa42967f5a7c2dbef688b37885 + fa26220dc800bcb1ae83d35ffca54a6dabba730764d60b1a4a506206efa380d7d1d89069778b082bb92396af4547024797797e01c927c78c9f70 + 750ef2002dfe1516baa4f165a3176942d35d9527f4b33505484130cd573f9d4a1f1e6656aff881aab482fb3d6151ab02f76267033f3feb9718fb + fed05a9b69a8d817a7e4a41efbe3ffeb355d1013778f14d4c30c92a386190fa23b388feddc635b22d8fa4998b65d483cd3b595553092123e144c + 49d91ddc2f7a88f3ef1ad2b0b19636bc3f50f61ea5157c73a1a5b956349b6cdf3ff50ec9ef7cbc1137b27d7839276a3ed4e778c505206669686e + f038b5808117fedf60ef3598e8ed1db1e5ad64f04af38e60e82fe04bc75594fd9fcd8bb79237adb9c9ffd3dc2c907345f874aec7055576a32263 + 486120ff62ad690a988919e941d33ed93706f6984032e205084cc46585b5aef035c22ddbb3b0ba04e83f80c1b06b4975f00207b357550d244051 + 89412ea6a83ad56c4873f499fdbdc761aa72".replace("\n", "").replace(" ", "")).unwrap(); + + let ss = hex::decode("2fae7214767890c4703fad953f5e3f91303111498caa135d77cde634151e71b5").unwrap(); + + let ss2 = decapsulate(ct.try_into().unwrap(), sk.try_into().unwrap()); + + assert_eq!(ss, ss2); + } +}