diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45481eb3..f87f442c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: stable + toolchain: 1.89.0 components: rustfmt, clippy - name: Run test script run: | @@ -21,7 +21,7 @@ jobs: uses: Cosmian/reusable_workflows/.github/workflows/cargo-publish.yml@develop if: startsWith(github.ref, 'refs/tags/') with: - toolchain: stable + toolchain: 1.89.0 secrets: inherit cleanup: needs: diff --git a/.gitignore b/.gitignore index a91b5abe..56cedb12 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ *nix* /*.sh /.vscode -Cargo.lock **/.#* **/#*# **/*~ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..f6dc25b8 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1055 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[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 = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", + "zeroize", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[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.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clap" +version = "4.5.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "cosmian_cover_crypt" +version = "15.0.0" +dependencies = [ + "cosmian_crypto_core", + "cosmian_openssl_provider", + "cosmian_rust_curve25519_provider", + "criterion", + "ml-kem", + "serde", + "serde_json", + "zeroize", +] + +[[package]] +name = "cosmian_crypto_core" +version = "10.4.0" +source = "git+https://github.com/Cosmian/crypto_core.git?branch=tbz%2Ffix-memory-allocation-error-upon-deserialization#24afc12b77b03ce476909a0835216a01bee32c5b" +dependencies = [ + "aead", + "aes-gcm", + "curve25519-dalek", + "ed25519-dalek", + "getrandom", + "leb128", + "rand_chacha", + "rand_core", + "sha2", + "signature", + "tiny-keccak", + "zeroize", +] + +[[package]] +name = "cosmian_openssl_provider" +version = "1.0.0" +source = "git+https://github.com/Cosmian/crypto_core.git?branch=tbz%2Ffix-memory-allocation-error-upon-deserialization#24afc12b77b03ce476909a0835216a01bee32c5b" +dependencies = [ + "cosmian_crypto_core", + "openssl", + "zeroize", +] + +[[package]] +name = "cosmian_rust_curve25519_provider" +version = "1.0.0" +source = "git+https://github.com/Cosmian/crypto_core.git?branch=tbz%2Ffix-memory-allocation-error-upon-deserialization#24afc12b77b03ce476909a0835216a01bee32c5b" +dependencies = [ + "cosmian_crypto_core", + "curve25519-dalek", + "zeroize", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rand_core", + "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 = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hybrid-array" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2d35805454dc9f8662a98d6d61886ffe26bd465f5960e0e55345c70d5c0d2a9" +dependencies = [ + "typenum", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "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 = "kem" +version = "0.3.0-pre.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8645470337db67b01a7f966decf7d0bafedbae74147d33e641c67a91df239f" +dependencies = [ + "rand_core", + "zeroize", +] + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "ml-kem" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcaee19a45f916d98f24a551cc9a2cdae705a040e66f3cbc4f3a282ea6a2e982" +dependencies = [ + "hybrid-array", + "kem", + "rand_core", + "sha3", + "zeroize", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[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 = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "indexmap", + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +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 = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" + +[[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 = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index ce0c398f..69c46cbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,22 +41,22 @@ required-features = ["test-utils"] default = ["mlkem-512", "curve25519"] mlkem-512 = [] mlkem-768 = [] -p-256 = ["elliptic-curve", "p256", "subtle"] -curve25519 = ["cosmian_crypto_core/curve25519"] +p-256 = [] +curve25519 = [] test-utils = [] [dependencies] -cosmian_crypto_core = { version = "10.3", default-features = false, features = [ +cosmian_openssl_provider = { git = "https://github.com/Cosmian/crypto_core.git", branch = "tbz/fix-memory-allocation-error-upon-deserialization" } +cosmian_rust_curve25519_provider = { git = "https://github.com/Cosmian/crypto_core.git", branch = "tbz/fix-memory-allocation-error-upon-deserialization" } +cosmian_crypto_core = { git = "https://github.com/Cosmian/crypto_core.git", branch = "tbz/fix-memory-allocation-error-upon-deserialization", default-features = false, features = [ "aes", "ser", "sha3" ] } -elliptic-curve = { version = "0.13", optional = true } ml-kem = { version = "0.2", features = ["zeroize"] } -p256 = { version = "0.13", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["preserve_order"] } -subtle = { version = "2.6", optional = true } +zeroize = "1.8" [dev-dependencies] criterion = { version = "0.7", features = [ diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000..98d44e9b --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,6 @@ +[toolchain] +# Pin to a stable toolchain (no nightly) because dependencies rely on +# `core::hint::select_unpredictable`, which is available on stable. +channel = "1.89.0" +profile = "minimal" +components = ["rustfmt", "clippy"] diff --git a/src/abe.rs b/src/abe.rs new file mode 100644 index 00000000..4daa330d --- /dev/null +++ b/src/abe.rs @@ -0,0 +1,17 @@ +mod policy; + +pub mod api; +pub mod core; +pub mod traits; + +pub mod encrypted_header; + +pub use api::Covercrypt; +pub use core::{MasterPublicKey, MasterSecretKey, UserSecretKey, XEnc}; +pub use policy::{ + AccessPolicy, AccessStructure, Attribute, Dimension, EncryptionHint, EncryptionStatus, + QualifiedAttribute, +}; + +#[cfg(any(test, feature = "test-utils"))] +pub use policy::gen_structure; diff --git a/src/api.rs b/src/abe/api.rs similarity index 83% rename from src/api.rs rename to src/abe/api.rs index c32776ba..3ff4732d 100644 --- a/src/api.rs +++ b/src/abe/api.rs @@ -1,33 +1,33 @@ -use std::sync::{Mutex, MutexGuard}; - -use cosmian_crypto_core::{ - reexport::{rand_core::SeedableRng, zeroize::Zeroizing}, - CsRng, Secret, SymmetricKey, -}; - -use super::{ - core::primitives::{prune, update_msk, usk_keygen}, - core::MIN_TRACING_LEVEL, - traits::AE, -}; use crate::{ - core::{ - primitives::{self, full_decaps, refresh, rekey, setup}, - MasterPublicKey, MasterSecretKey, UserSecretKey, XEnc, SHARED_SECRET_LENGTH, + abe::{ + core::{ + primitives::{ + self, master_decaps, prune, refresh, rekey, setup, update_msk, usk_keygen, + }, + MasterPublicKey, MasterSecretKey, UserSecretKey, XEnc, MIN_TRACING_LEVEL, + SHARED_SECRET_LENGTH, + }, + policy::AccessPolicy, + traits::{KemAc, PkeAc}, }, - traits::{KemAc, PkeAc}, - AccessPolicy, Error, + Error, +}; +use cosmian_crypto_core::{ + reexport::rand_core::{RngCore, SeedableRng}, + traits::AE, + CsRng, Secret, SymmetricKey, }; +use std::sync::{Arc, Mutex, MutexGuard}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Covercrypt { - rng: Mutex, + rng: Arc>, } impl Default for Covercrypt { fn default() -> Self { Self { - rng: Mutex::new(CsRng::from_entropy()), + rng: Arc::new(Mutex::new(CsRng::from_entropy())), } } } @@ -158,7 +158,7 @@ impl Covercrypt { mpk: &MasterPublicKey, encapsulation: &XEnc, ) -> Result<(Secret<32>, XEnc), Error> { - let (_ss, rights) = full_decaps(msk, encapsulation)?; + let (_ss, rights) = master_decaps(msk, encapsulation, true)?; primitives::encaps( &mut *self.rng.lock().expect("Mutex lock failed!"), mpk, @@ -194,12 +194,18 @@ impl KemAc for Covercrypt { } } -impl> PkeAc - for Covercrypt +impl< + const KEY_LENGTH: usize, + const NONCE_LENGTH: usize, + const TAG_LENGTH: usize, + E: AE, + > PkeAc for Covercrypt +where + Error: From, { type EncryptionKey = MasterPublicKey; type DecryptionKey = UserSecretKey; - type Ciphertext = (XEnc, Vec); + type Ciphertext = (XEnc, E::Ciphertext); type Error = Error; fn encrypt( @@ -212,19 +218,22 @@ impl> PkeAc::derive(&seed, b"Covercrypt AE key")?; + let mut nonce = [0; NONCE_LENGTH]; + rng.fill_bytes(&mut nonce); + let ctx = E::encrypt(&key, ptx, &nonce)?; + Ok((enc, ctx)) } fn decrypt( &self, usk: &Self::DecryptionKey, ctx: &Self::Ciphertext, - ) -> Result>>, Self::Error> { + ) -> Result, Self::Error> { self.decaps(usk, &ctx.0)? .map(|seed| { let key = SymmetricKey::derive(&seed, b"Covercrypt AE key")?; - E::decrypt(&key, &ctx.1) + E::decrypt(&key, ctx.1.as_ref()).map_err(Self::Error::from) }) .transpose() } diff --git a/src/core/mod.rs b/src/abe/core/mod.rs similarity index 72% rename from src/core/mod.rs rename to src/abe/core/mod.rs index 95c13866..da0482e3 100644 --- a/src/core/mod.rs +++ b/src/abe/core/mod.rs @@ -1,23 +1,18 @@ #![allow(non_snake_case)] -use std::{ - collections::{HashMap, HashSet, LinkedList}, - hash::Hash, -}; - -use cosmian_crypto_core::{reexport::rand_core::CryptoRngCore, SymmetricKey}; -use kem::MlKem; -use nike::ElGamal; - use crate::{ - abe_policy::{AccessStructure, EncryptionStatus, Right}, + abe::policy::{AccessStructure, EncryptionHint, EncryptionStatus, Right}, data_struct::{RevisionMap, RevisionVec}, - traits::{Kem, Nike, Sampling, Zero}, - Error, SecurityMode, + providers::{ElGamal, MlKem}, + Error, +}; +use cosmian_crypto_core::{ + reexport::{rand_core::CryptoRngCore, zeroize::ZeroizeOnDrop}, + traits::{Sampling, Zero, KEM, NIKE}, + SymmetricKey, }; +use std::collections::{HashMap, HashSet, LinkedList}; -mod kem; -mod nike; mod serialization; #[cfg(test)] @@ -59,32 +54,32 @@ pub const MIN_TRACING_LEVEL: usize = 1; #[derive(Clone, Debug, PartialEq)] enum RightSecretKey { PreQuantum { - sk: ::SecretKey, + sk: ::SecretKey, }, PostQuantum { - dk: ::DecapsulationKey, + dk: >::DecapsulationKey, }, Hybridized { - sk: ::SecretKey, - dk: ::DecapsulationKey, + sk: ::SecretKey, + dk: >::DecapsulationKey, }, } impl RightSecretKey { /// Generates a new random right secret key cryptographically bound to the Covercrypt binding /// point `h`. - fn random(rng: &mut impl CryptoRngCore, security_mode: SecurityMode) -> Result { + fn random(rng: &mut impl CryptoRngCore, security_mode: EncryptionHint) -> Result { match security_mode { - SecurityMode::PreQuantum => { - let sk = ::SecretKey::random(rng); + EncryptionHint::Classic => { + let sk = ::SecretKey::random(rng); Ok(Self::PreQuantum { sk }) } - SecurityMode::PostQuantum => { + EncryptionHint::PostQuantum => { let (dk, _) = MlKem::keygen(rng)?; Ok(Self::PostQuantum { dk }) } - SecurityMode::Hybridized => { - let sk = ::SecretKey::random(rng); + EncryptionHint::Hybridized => { + let sk = ::SecretKey::random(rng); let (dk, _) = MlKem::keygen(rng)?; Ok(Self::Hybridized { sk, dk }) } @@ -93,7 +88,7 @@ impl RightSecretKey { /// Generates the associated right public key. #[must_use] - fn cpk(&self, h: &::PublicKey) -> RightPublicKey { + fn cpk(&self, h: &::PublicKey) -> RightPublicKey { match self { Self::Hybridized { sk, dk } => RightPublicKey::Hybridized { H: h * sk, @@ -105,39 +100,41 @@ impl RightSecretKey { } /// Returns the security mode of this right secret key. - fn security_mode(&self) -> SecurityMode { + fn security_mode(&self) -> EncryptionHint { match self { - Self::Hybridized { .. } => SecurityMode::Hybridized, - Self::PostQuantum { .. } => SecurityMode::PostQuantum, - Self::PreQuantum { .. } => SecurityMode::PreQuantum, + Self::Hybridized { .. } => EncryptionHint::Hybridized, + Self::PostQuantum { .. } => EncryptionHint::PostQuantum, + Self::PreQuantum { .. } => EncryptionHint::Classic, } } /// Sets the security mode of this right secret key. fn set_security_mode( self, - security_mode: SecurityMode, + security_mode: EncryptionHint, rng: &mut impl CryptoRngCore, ) -> Result { Ok(match (self, security_mode) { - (Self::Hybridized { sk, .. }, SecurityMode::PreQuantum) => Self::PreQuantum { sk }, - (Self::Hybridized { dk, .. }, SecurityMode::PostQuantum) => Self::PostQuantum { dk }, - (Self::Hybridized { sk, dk }, SecurityMode::Hybridized) => Self::Hybridized { sk, dk }, - (Self::PostQuantum { .. }, SecurityMode::PreQuantum) => Self::PostQuantum { - dk: ::keygen(rng)?.0, + (Self::Hybridized { sk, .. }, EncryptionHint::Classic) => Self::PreQuantum { sk }, + (Self::Hybridized { dk, .. }, EncryptionHint::PostQuantum) => Self::PostQuantum { dk }, + (Self::Hybridized { sk, dk }, EncryptionHint::Hybridized) => { + Self::Hybridized { sk, dk } + } + (Self::PostQuantum { .. }, EncryptionHint::Classic) => Self::PostQuantum { + dk: >::keygen(rng)?.0, }, - (Self::PostQuantum { dk }, SecurityMode::PostQuantum) => Self::PostQuantum { dk }, - (Self::PostQuantum { dk }, SecurityMode::Hybridized) => Self::Hybridized { - sk: ::keygen(rng)?.0, + (Self::PostQuantum { dk }, EncryptionHint::PostQuantum) => Self::PostQuantum { dk }, + (Self::PostQuantum { dk }, EncryptionHint::Hybridized) => Self::Hybridized { + sk: ::keygen(rng)?.0, dk, }, - (Self::PreQuantum { sk }, SecurityMode::PreQuantum) => Self::PreQuantum { sk }, - (Self::PreQuantum { .. }, SecurityMode::PostQuantum) => Self::PostQuantum { - dk: ::keygen(rng)?.0, + (Self::PreQuantum { sk }, EncryptionHint::Classic) => Self::PreQuantum { sk }, + (Self::PreQuantum { .. }, EncryptionHint::PostQuantum) => Self::PostQuantum { + dk: >::keygen(rng)?.0, }, - (Self::PreQuantum { sk }, SecurityMode::Hybridized) => Self::Hybridized { + (Self::PreQuantum { sk }, EncryptionHint::Hybridized) => Self::Hybridized { sk, - dk: ::keygen(rng)?.0, + dk: >::keygen(rng)?.0, }, }) } @@ -151,31 +148,31 @@ impl RightSecretKey { #[derive(Clone, Debug, PartialEq)] enum RightPublicKey { PreQuantum { - H: ::PublicKey, + H: ::PublicKey, }, PostQuantum { - ek: ::EncapsulationKey, + ek: >::EncapsulationKey, }, Hybridized { - H: ::PublicKey, - ek: ::EncapsulationKey, + H: ::PublicKey, + ek: >::EncapsulationKey, }, } impl RightPublicKey { /// Returns the security mode of this right public key. - pub fn security_mode(&self) -> SecurityMode { + pub fn security_mode(&self) -> EncryptionHint { match self { - Self::Hybridized { .. } => SecurityMode::Hybridized, - Self::PostQuantum { .. } => SecurityMode::PostQuantum, - Self::PreQuantum { .. } => SecurityMode::PreQuantum, + Self::Hybridized { .. } => EncryptionHint::Hybridized, + Self::PostQuantum { .. } => EncryptionHint::PostQuantum, + Self::PreQuantum { .. } => EncryptionHint::Classic, } } } /// Covercrypt user IDs are used to make user keys unique and traceable. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)] -struct UserId(LinkedList<::SecretKey>); +#[derive(Clone, Debug, PartialEq, Eq, Default)] +struct UserId(LinkedList<::SecretKey>); impl UserId { /// Returns the tracing level of the USK. @@ -183,7 +180,7 @@ impl UserId { self.0.len() - 1 } - fn iter(&self) -> impl Iterator::SecretKey> { + fn iter(&self) -> impl Iterator::SecretKey> { self.0.iter() } } @@ -204,18 +201,29 @@ impl UserId { /// - the set of known user IDs. #[derive(Debug, PartialEq, Eq)] struct TracingSecretKey { - s: ::SecretKey, - tracers: LinkedList<(::SecretKey, ::PublicKey)>, - users: HashSet, + s: ::SecretKey, + tracers: LinkedList<(::SecretKey, ::PublicKey)>, + // Since `Hash` is not a fallible operation, it cannot be implemented on FFI + // providers like OpenSSL. And since `Zeroizing>` does not implement + // `Hash` either, a `HashSet` cannot be using without extracting the raw + // bytes which then requires manually zeroizing them everywhere they may be + // leaking. Using a linked list implies a linear complexity in the number of + // comparisons, which themselves have a linear complexity in the tracing + // dimension. This is not ideal, but it is safe. + // + // Since this is an internal implementation detail, the container used may + // be change later without breaking change as long as it serializes to the + // same bytes. + users: LinkedList, } impl TracingSecretKey { fn new_with_level(level: usize, rng: &mut impl CryptoRngCore) -> Result { - let s = ::SecretKey::random(rng); + let s = ::SecretKey::random(rng); let tracers = (0..=level) - .map(|_| ElGamal::keygen(rng)) + .map(|_| ::keygen(rng)) .collect::>()?; - let users = HashSet::new(); + let users = LinkedList::new(); Ok(Self { s, tracers, users }) } @@ -227,14 +235,14 @@ impl TracingSecretKey { /// Generates a new tracer. Returns the associated trap. fn _increase_tracing(&mut self, rng: &mut impl CryptoRngCore) -> Result<(), Error> { - self.tracers.push_back(ElGamal::keygen(rng)?); + self.tracers.push_back(::keygen(rng)?); Ok(()) } /// Drops the oldest tracer and returns it. fn _decrease_tracing( &mut self, - ) -> Result<(::SecretKey, ::PublicKey), Error> { + ) -> Result<(::SecretKey, ::PublicKey), Error> { if self.tracing_level() == MIN_TRACING_LEVEL { Err(Error::OperationNotPermitted(format!( "tracing level cannot be lower than {MIN_TRACING_LEVEL}" @@ -272,14 +280,17 @@ impl TracingSecretKey { /// Adds the given user ID to the list of known users. fn add_user(&mut self, id: UserId) { - self.users.insert(id); + self.users.push_front(id); } /// Removes the given user ID from the list of known users. /// /// Returns true if the user was in the list. fn del_user(&mut self, id: &UserId) -> bool { - self.users.remove(id) + self.users + .extract_if(|id_| id == id_) + .collect::>() + .is_empty() } /// Generates the associated tracing public key. @@ -289,7 +300,7 @@ impl TracingSecretKey { } /// Returns the binding points. - fn binding_point(&self) -> ::PublicKey { + fn binding_point(&self) -> ::PublicKey { (&self.s).into() } @@ -301,7 +312,7 @@ impl TracingSecretKey { .tracers .iter() .take(self.tracers.len() - 1) - .map(|_| ::SecretKey::random(rng)) + .map(|_| ::SecretKey::random(rng)) .collect::>(); let last_marker = ((&self.s @@ -310,7 +321,7 @@ impl TracingSecretKey { .iter() .zip(markers.iter()) .map(|((sk_i, _), a_i)| sk_i * a_i) - .fold(::SecretKey::zero(), |acc, x_i| acc + x_i)) + .fold(::SecretKey::zero(), |acc, x_i| acc + x_i)) / last_tracer)?; markers.push_back(last_marker); @@ -358,8 +369,8 @@ impl TracingSecretKey { } /// Covercrypt tracing public key. -#[derive(Debug, PartialEq, Eq, Default)] -struct TracingPublicKey(LinkedList<::PublicKey>); +#[derive(Debug, Clone, PartialEq, Eq, Default)] +struct TracingPublicKey(LinkedList<::PublicKey>); impl TracingPublicKey { /// Returns the tracing level tracing of this key. @@ -384,6 +395,9 @@ pub struct MasterSecretKey { pub access_structure: AccessStructure, } +// All secret keys are zeroized on drop. +impl ZeroizeOnDrop for MasterSecretKey {} + impl MasterSecretKey { /// Returns the most recent secret key associated to each given right. /// @@ -403,7 +417,7 @@ impl MasterSecretKey { }) } - fn tracing_points(&self) -> impl IntoIterator::PublicKey> { + fn tracing_points(&self) -> impl IntoIterator::PublicKey> { self.tsk.tracers.iter().map(|(_, P)| P) } @@ -436,7 +450,7 @@ impl MasterSecretKey { /// - the tracing public key; /// - the public keys for each right in Omega; /// - the access structure. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct MasterPublicKey { tpk: TracingPublicKey, encryption_keys: HashMap, @@ -451,7 +465,7 @@ impl MasterPublicKey { /// Generates traps for the given scalar. // TODO: find a better concept. - fn set_traps(&self, r: &::SecretKey) -> Vec<::PublicKey> { + fn set_traps(&self, r: &::SecretKey) -> Vec<::PublicKey> { self.tpk.0.iter().map(|Pi| Pi * r).collect() } @@ -465,7 +479,7 @@ impl MasterPublicKey { fn select_subkeys( &self, targets: &HashSet, - ) -> Result<(SecurityMode, Vec<&RightPublicKey>), Error> { + ) -> Result<(EncryptionHint, Vec<&RightPublicKey>), Error> { let subkeys = targets .iter() .map(|r| { @@ -508,11 +522,14 @@ impl MasterPublicKey { #[derive(Clone, Debug, PartialEq)] pub struct UserSecretKey { id: UserId, - ps: Vec<::PublicKey>, + ps: Vec<::PublicKey>, secrets: RevisionVec, signature: Option, } +// All secret keys are zeroized on drop. +impl ZeroizeOnDrop for UserSecretKey {} + impl UserSecretKey { /// Returns the tracing level of this user secret key. pub fn tracing_level(&self) -> usize { @@ -524,7 +541,7 @@ impl UserSecretKey { self.secrets.len() } - fn tracing_points(&self) -> &[::PublicKey] { + fn tracing_points(&self) -> &[::PublicKey] { &self.ps } } @@ -541,17 +558,23 @@ impl UserSecretKey { pub enum XEnc { PreQuantum { tag: Tag, - c: Vec<::PublicKey>, + c: Vec<::PublicKey>, encapsulations: Vec<[u8; SHARED_SECRET_LENGTH]>, }, PostQuantum { tag: Tag, - encapsulations: Vec<(::Encapsulation, [u8; SHARED_SECRET_LENGTH])>, + encapsulations: Vec<( + >::Encapsulation, + [u8; SHARED_SECRET_LENGTH], + )>, }, Hybridized { tag: Tag, - c: Vec<::PublicKey>, - encapsulations: Vec<(::Encapsulation, [u8; SHARED_SECRET_LENGTH])>, + c: Vec<::PublicKey>, + encapsulations: Vec<( + >::Encapsulation, + [u8; SHARED_SECRET_LENGTH], + )>, }, } @@ -573,11 +596,11 @@ impl XEnc { } } - pub fn security_mode(&self) -> SecurityMode { + pub fn security_mode(&self) -> EncryptionHint { match self { - Self::Hybridized { .. } => SecurityMode::Hybridized, - Self::PostQuantum { .. } => SecurityMode::PostQuantum, - Self::PreQuantum { .. } => SecurityMode::PreQuantum, + Self::Hybridized { .. } => EncryptionHint::Hybridized, + Self::PostQuantum { .. } => EncryptionHint::PostQuantum, + Self::PreQuantum { .. } => EncryptionHint::Classic, } } } diff --git a/src/core/primitives.rs b/src/abe/core/primitives.rs similarity index 89% rename from src/core/primitives.rs rename to src/abe/core/primitives.rs index dae4acc3..f8dde602 100644 --- a/src/core/primitives.rs +++ b/src/abe/core/primitives.rs @@ -1,28 +1,28 @@ -use std::{ - collections::{HashMap, HashSet, LinkedList}, - mem::take, +use crate::{ + abe::{ + core::{ + KmacSignature, MasterPublicKey, MasterSecretKey, RightPublicKey, RightSecretKey, + TracingSecretKey, UserId, UserSecretKey, XEnc, MIN_TRACING_LEVEL, SHARED_SECRET_LENGTH, + SIGNATURE_LENGTH, SIGNING_KEY_LENGTH, TAG_LENGTH, + }, + policy::{AccessStructure, EncryptionHint, EncryptionStatus, Right}, + }, + data_struct::{RevisionMap, RevisionVec}, + providers::{ElGamal, MlKem}, + Error, }; - use cosmian_crypto_core::{ bytes_ser_de::Serializable, reexport::{ rand_core::{CryptoRngCore, RngCore}, tiny_keccak::{Hasher, Kmac, Sha3}, - zeroize::Zeroize, }, + traits::{Seedable, KEM, NIKE}, RandomFixedSizeCBytes, Secret, SymmetricKey, }; - -use crate::{ - abe_policy::{AccessStructure, EncryptionStatus, Right, SecurityMode}, - core::{ - kem::MlKem, nike::ElGamal, KmacSignature, MasterPublicKey, MasterSecretKey, RightPublicKey, - RightSecretKey, TracingSecretKey, UserId, UserSecretKey, XEnc, MIN_TRACING_LEVEL, - SHARED_SECRET_LENGTH, SIGNATURE_LENGTH, SIGNING_KEY_LENGTH, TAG_LENGTH, - }, - data_struct::{RevisionMap, RevisionVec}, - traits::{Kem, Nike, Sampling}, - Error, +use std::{ + collections::{HashMap, HashSet, LinkedList}, + mem::take, }; fn xor_2(lhs: &[u8; LENGTH], rhs: &[u8; LENGTH]) -> [u8; LENGTH] { @@ -82,13 +82,15 @@ fn verify(msk: &MasterSecretKey, usk: &UserSecretKey) -> Result<(), Error> { } } -fn G_hash(seed: &Secret) -> Result<::SecretKey, Error> { - Ok(<::SecretKey as Sampling>::hash(&**seed)) +fn G_hash(seed: &Secret) -> Result<::SecretKey, Error> { + Ok(<::SecretKey as Seedable< + SHARED_SECRET_LENGTH, + >>::from_seed(seed)) } fn H_hash( - K1: Option<&::PublicKey>, - K2: Option<&Secret>, + K1: Option<&::PublicKey>, + K2: Option<&SymmetricKey>, T: &Secret, ) -> Result, Error> { // Additional check to enforce the constraint on the SHARED_SECRET_LENGTH @@ -128,8 +130,10 @@ fn J_hash( } fn generate_T<'a>( - c: Option<&[::PublicKey]>, - encapsulations: Option::Encapsulation>>, + c: Option<&[::PublicKey]>, + encapsulations: Option< + impl IntoIterator>::Encapsulation>, + >, ) -> Result, Error> { let mut hasher = Sha3::v256(); let mut T = Secret::new(); @@ -212,12 +216,12 @@ pub fn usk_keygen( /// marker c, ElGamal random r and subkeys. fn h_encaps<'a>( S: Secret, - c: Vec<::PublicKey>, - r: ::SecretKey, + c: Vec<::PublicKey>, + r: ::SecretKey, subkeys: impl IntoIterator< Item = ( - &'a ::PublicKey, - &'a ::EncapsulationKey, + &'a ::PublicKey, + &'a >::EncapsulationKey, ), >, rng: &mut impl CryptoRngCore, @@ -225,7 +229,7 @@ fn h_encaps<'a>( let encs = subkeys .into_iter() .map(|(H, ek)| { - let K1 = ElGamal::session_key(&r, H)?; + let K1 = ElGamal::shared_secret(&r, H)?; let (K2, E) = MlKem::enc(ek, rng)?; Ok((K1, K2, E)) }) @@ -235,9 +239,8 @@ fn h_encaps<'a>( let encapsulations = encs .into_iter() - .map(|(mut K1, K2, E)| -> Result<_, _> { + .map(|(K1, K2, E)| -> Result<_, _> { let F = xor_2(&S, &*H_hash(Some(&K1), Some(&K2), &T)?); - K1.zeroize(); Ok((E, F)) }) .collect::, Error>>()?; @@ -260,7 +263,7 @@ fn h_encaps<'a>( /// subkeys. fn post_quantum_encaps<'a>( S: Secret, - subkeys: impl IntoIterator::EncapsulationKey>, + subkeys: impl IntoIterator>::EncapsulationKey>, rng: &mut impl CryptoRngCore, ) -> Result<(Secret, XEnc), Error> { let encs = subkeys @@ -295,16 +298,16 @@ fn post_quantum_encaps<'a>( /// marker c, ElGamal random r and subkeys. fn pre_quantum_encaps<'a>( S: Secret, - c: Vec<::PublicKey>, - r: ::SecretKey, - subkeys: impl IntoIterator::PublicKey>, + c: Vec<::PublicKey>, + r: ::SecretKey, + subkeys: impl IntoIterator::PublicKey>, ) -> Result<(Secret, XEnc), Error> { let T = generate_T(Some(&c), None::>)?; let encapsulations = subkeys .into_iter() .map(|H| -> Result<_, _> { - let K1 = ElGamal::session_key(&r, H)?; + let K1 = ElGamal::shared_secret(&r, H)?; let F = xor_2(&S, &*H_hash(Some(&K1), None, &T)?); Ok(F) }) @@ -348,7 +351,7 @@ pub fn encaps( let S = Secret::random(rng); match is_hybridized { - SecurityMode::PreQuantum => { + EncryptionHint::Classic => { let r = G_hash(&S)?; let c = mpk.set_traps(&r); @@ -361,7 +364,7 @@ pub fn encaps( }); pre_quantum_encaps(S, c, r, subkeys) } - SecurityMode::PostQuantum => { + EncryptionHint::PostQuantum => { let subkeys = coordinate_keys.into_iter().map(|subkey| { if let RightPublicKey::PostQuantum { ek } = subkey { ek @@ -371,7 +374,7 @@ pub fn encaps( }); post_quantum_encaps(S, subkeys, rng) } - SecurityMode::Hybridized => { + EncryptionHint::Hybridized => { let r = G_hash(&S)?; let c = mpk.set_traps(&r); @@ -390,18 +393,17 @@ pub fn encaps( #[allow(clippy::too_many_arguments)] fn attempt_pre_quantum_decaps<'a>( secret: &RightSecretKey, - A: &::PublicKey, + A: &::PublicKey, U: &Secret, T: &Secret, F: &[u8; 32], - c: &[::PublicKey], + c: &[::PublicKey], tag: &[u8; TAG_LENGTH], - tracing_points: impl IntoIterator::PublicKey>, + tracing_points: impl IntoIterator::PublicKey>, ) -> Result>, Error> { if let RightSecretKey::PreQuantum { sk } = secret { - let mut K1 = ElGamal::session_key(sk, A)?; + let K1 = ElGamal::shared_secret(sk, A)?; let S = xor_in_place(H_hash(Some(&K1), None, T)?, F); - K1.zeroize(); let (tag_ij, ss) = J_hash(&S, U); if tag == &tag_ij { // Fujisaki-Okamoto @@ -421,17 +423,17 @@ fn attempt_pre_quantum_decaps<'a>( #[allow(clippy::too_many_arguments)] fn attempt_hybridized_decaps<'a>( secret: &RightSecretKey, - A: &::PublicKey, + A: &::PublicKey, U: &Secret, T: &Secret, - E: &::Encapsulation, + E: &>::Encapsulation, F: &[u8; 32], - c: &[::PublicKey], + c: &[::PublicKey], tag: &[u8; TAG_LENGTH], - tracing_points: impl IntoIterator::PublicKey>, + tracing_points: impl IntoIterator::PublicKey>, ) -> Result>, Error> { if let RightSecretKey::Hybridized { sk, dk } = secret { - let mut K1 = ElGamal::session_key(sk, A)?; + let K1 = ElGamal::shared_secret(sk, A)?; let K2 = MlKem::dec(dk, E)?; let S_ij = xor_in_place(H_hash(Some(&K1), Some(&K2), T)?, F); let (tag_ij, ss) = J_hash(&S_ij, U); @@ -443,7 +445,6 @@ fn attempt_hybridized_decaps<'a>( .map(|P| P * &r) .collect::>(); if c == c_ij { - K1.zeroize(); return Ok(Some(ss)); } } @@ -455,7 +456,7 @@ fn attempt_post_quantum_decaps( secret: &RightSecretKey, U: &Secret, T: &Secret, - E: &::Encapsulation, + E: &>::Encapsulation, F: &[u8; 32], tag: &[u8; TAG_LENGTH], ) -> Result>, Error> { @@ -479,20 +480,23 @@ pub fn decaps( ) -> Result>, Error> { fn generate_tracing_closure( usk: &UserSecretKey, - c: &[::PublicKey], - ) -> ::PublicKey { + c: &[::PublicKey], + ) -> ::PublicKey { usk.id .iter() .zip(c.iter()) .map(|(marker, trap)| trap * marker) - .sum::<::PublicKey>() + .sum::<::PublicKey>() } fn partial_post_quantum_decaps( rng: &mut impl CryptoRngCore, usk: &UserSecretKey, tag: &[u8; TAG_LENGTH], - encs: &[(::Encapsulation, [u8; SHARED_SECRET_LENGTH])], + encs: &[( + >::Encapsulation, + [u8; SHARED_SECRET_LENGTH], + )], ) -> Result>, Error> { let T = generate_T(None, Some(encs.iter().map(|(E, _)| E)))?; let U = generate_U(&T, encs.iter().map(|(_, F)| F)); @@ -522,9 +526,12 @@ pub fn decaps( fn partial_hybridized_decaps( rng: &mut impl CryptoRngCore, usk: &UserSecretKey, - c: &[::PublicKey], + c: &[::PublicKey], tag: &[u8; TAG_LENGTH], - encs: &[(::Encapsulation, [u8; SHARED_SECRET_LENGTH])], + encs: &[( + >::Encapsulation, + [u8; SHARED_SECRET_LENGTH], + )], ) -> Result>, Error> { let A = generate_tracing_closure(usk, c); let T = generate_T(Some(c), Some(encs.iter().map(|(E, _)| E)))?; @@ -565,7 +572,7 @@ pub fn decaps( fn partial_pre_quantum_decaps( rng: &mut impl CryptoRngCore, usk: &UserSecretKey, - c: &[::PublicKey], + c: &[::PublicKey], tag: &[u8; TAG_LENGTH], encs: &Vec<[u8; SHARED_SECRET_LENGTH]>, ) -> Result>, Error> { @@ -624,9 +631,10 @@ pub fn decaps( /// Recover the encapsulated shared secret and set of rights used in the /// encapsulation. -pub fn full_decaps( +pub fn master_decaps( msk: &MasterSecretKey, encapsulation: &XEnc, + full: bool, ) -> Result<(Secret, HashSet), Error> { /// Opens the given encapsulation with the provided secrets. Returns both /// the encapsulated secret and the right associated to the first secret @@ -648,8 +656,8 @@ pub fn full_decaps( fn generate_tracing_closure( msk: &MasterSecretKey, - c: &[::PublicKey], - ) -> Result<::PublicKey, Error> { + c: &[::PublicKey], + ) -> Result<::PublicKey, Error> { let c_0 = c .first() .ok_or_else(|| Error::Kem("invalid encapsulation: C is empty".to_string()))?; @@ -663,11 +671,12 @@ pub fn full_decaps( Ok(c_0 * &(&msk.tsk.s / t_0)?) } - fn full_pre_quantum_decapsulation( + fn pre_quantum_decapsulation( msk: &MasterSecretKey, tag: &[u8; TAG_LENGTH], - c: &[::PublicKey], + c: &[::PublicKey], encapsulations: &[[u8; 32]], + full: bool, ) -> Result<(Secret, HashSet), Error> { let A = generate_tracing_closure(msk, c)?; let T = generate_T(Some(c), None::>)?; @@ -696,6 +705,10 @@ pub fn full_decaps( secrets.remove(&right); enc_ss = Some(ss); rights.insert(right); + + if !full { + break; + } } enc_ss @@ -705,10 +718,14 @@ pub fn full_decaps( .ok_or_else(|| Error::Kem("empty encapsulation".to_string())) } - fn full_post_quantum_decapsulation( + fn post_quantum_decapsulation( msk: &MasterSecretKey, tag: &[u8; TAG_LENGTH], - encapsulations: &[(::Encapsulation, [u8; SHARED_SECRET_LENGTH])], + encapsulations: &[( + >::Encapsulation, + [u8; SHARED_SECRET_LENGTH], + )], + full: bool, ) -> Result<(Secret, HashSet), Error> { let T = generate_T(None, Some(encapsulations.iter().map(|(E, _)| E)))?; let U = generate_U(&T, encapsulations.iter().map(|(_, F)| F)); @@ -734,6 +751,10 @@ pub fn full_decaps( secrets.remove(&right); enc_ss = Some(ss); rights.insert(right); + + if !full { + break; + } } enc_ss @@ -743,11 +764,15 @@ pub fn full_decaps( .ok_or_else(|| Error::Kem("empty encapsulation".to_string())) } - fn full_hybrid_decapsulation( + fn hybrid_decapsulation( msk: &MasterSecretKey, tag: &[u8; TAG_LENGTH], - c: &[::PublicKey], - encapsulations: &[(::Encapsulation, [u8; SHARED_SECRET_LENGTH])], + c: &[::PublicKey], + encapsulations: &[( + >::Encapsulation, + [u8; SHARED_SECRET_LENGTH], + )], + full: bool, ) -> Result<(Secret, HashSet), Error> { let A = generate_tracing_closure(msk, c)?; let T = generate_T(Some(c), Some(encapsulations.iter().map(|(E, _)| E)))?; @@ -775,6 +800,10 @@ pub fn full_decaps( secrets.remove(&right); enc_ss = Some(ss); rights.insert(right); + + if !full { + break; + } } enc_ss @@ -789,16 +818,16 @@ pub fn full_decaps( tag, c, encapsulations, - } => full_pre_quantum_decapsulation(msk, tag, c, encapsulations), + } => pre_quantum_decapsulation(msk, tag, c, encapsulations, full), XEnc::PostQuantum { tag, encapsulations, - } => full_post_quantum_decapsulation(msk, tag, encapsulations), + } => post_quantum_decapsulation(msk, tag, encapsulations, full), XEnc::Hybridized { tag, c, encapsulations, - } => full_hybrid_decapsulation(msk, tag, c, encapsulations), + } => hybrid_decapsulation(msk, tag, c, encapsulations, full), } } @@ -808,7 +837,7 @@ pub fn full_decaps( pub fn update_msk( rng: &mut impl CryptoRngCore, msk: &mut MasterSecretKey, - rights: HashMap, + rights: HashMap, ) -> Result<(), Error> { let mut secrets = take(&mut msk.secrets); secrets.retain(|r| rights.contains_key(r)); diff --git a/src/core/serialization/mod.rs b/src/abe/core/serialization/mod.rs similarity index 94% rename from src/core/serialization/mod.rs rename to src/abe/core/serialization/mod.rs index aaa16fdc..ec090b28 100644 --- a/src/core/serialization/mod.rs +++ b/src/abe/core/serialization/mod.rs @@ -3,7 +3,7 @@ use cosmian_crypto_core::bytes_ser_de::{Deserializer, Serializable, Serializer}; use crate::{ - core::{ + abe::core::{ MasterPublicKey, MasterSecretKey, RightPublicKey, RightSecretKey, TracingPublicKey, TracingSecretKey, UserId, UserSecretKey, XEnc, }, @@ -291,15 +291,16 @@ mod tests { }; use crate::{ - abe_policy::{EncryptionStatus, Right}, - api::Covercrypt, - core::{ - primitives::{encaps, rekey, setup, update_msk, usk_keygen}, - MIN_TRACING_LEVEL, + abe::{ + api::Covercrypt, + core::{ + primitives::{encaps, rekey, setup, update_msk, usk_keygen}, + MIN_TRACING_LEVEL, + }, + policy::{AccessPolicy, EncryptionHint, EncryptionStatus, Right}, + traits::KemAc, }, test_utils::cc_keygen, - traits::KemAc, - AccessPolicy, SecurityMode, }; #[test] @@ -313,15 +314,15 @@ mod tests { let universe = HashMap::from([ ( coordinate_1.clone(), - (SecurityMode::Hybridized, EncryptionStatus::EncryptDecrypt), + (EncryptionHint::Hybridized, EncryptionStatus::EncryptDecrypt), ), ( coordinate_2.clone(), - (SecurityMode::Hybridized, EncryptionStatus::EncryptDecrypt), + (EncryptionHint::Hybridized, EncryptionStatus::EncryptDecrypt), ), ( coordinate_3.clone(), - (SecurityMode::Hybridized, EncryptionStatus::EncryptDecrypt), + (EncryptionHint::Hybridized, EncryptionStatus::EncryptDecrypt), ), ]); diff --git a/src/core/tests.rs b/src/abe/core/tests.rs similarity index 87% rename from src/core/tests.rs rename to src/abe/core/tests.rs index e038938b..ffe38412 100644 --- a/src/core/tests.rs +++ b/src/abe/core/tests.rs @@ -1,16 +1,18 @@ use std::collections::{HashMap, HashSet}; -use cosmian_crypto_core::{reexport::rand_core::SeedableRng, Aes256Gcm, CsRng}; +use cosmian_crypto_core::{reexport::rand_core::SeedableRng, traits::AE_InPlace, Aes256Gcm, CsRng}; use crate::{ - abe_policy::{AccessPolicy, EncryptionStatus, Right}, - api::Covercrypt, - core::{ - primitives::{decaps, encaps, refresh, rekey, update_msk}, - SecurityMode, + abe::{ + core::{ + primitives::{decaps, encaps, refresh, rekey, update_msk}, + EncryptionHint, + }, + policy::{AccessPolicy, EncryptionStatus, Right}, + traits::{KemAc, PkeAc}, + Covercrypt, }, test_utils::cc_keygen, - traits::{KemAc, PkeAc}, }; use super::{ @@ -20,8 +22,8 @@ use super::{ #[test] fn security_mode_ordering() { - assert!(SecurityMode::PreQuantum < SecurityMode::PostQuantum); - assert!(SecurityMode::PostQuantum < SecurityMode::Hybridized); + assert!(EncryptionHint::Classic < EncryptionHint::PostQuantum); + assert!(EncryptionHint::PostQuantum < EncryptionHint::Hybridized); } /// This test asserts that it is possible to encapsulate a key for a given @@ -40,11 +42,11 @@ fn test_encapsulation() { HashMap::from_iter([ ( other_coordinate.clone(), - (SecurityMode::PreQuantum, EncryptionStatus::EncryptDecrypt), + (EncryptionHint::Classic, EncryptionStatus::EncryptDecrypt), ), ( target_coordinate.clone(), - (SecurityMode::PreQuantum, EncryptionStatus::EncryptDecrypt), + (EncryptionHint::Classic, EncryptionStatus::EncryptDecrypt), ), ]), ) @@ -102,7 +104,7 @@ fn test_update() { .map(|_| { ( Right::random(&mut rng), - (SecurityMode::PreQuantum, EncryptionStatus::EncryptDecrypt), + (EncryptionHint::Classic, EncryptionStatus::EncryptDecrypt), ) }) .collect::>(); @@ -156,11 +158,11 @@ fn test_rekey() { HashMap::from_iter([ ( coordinate_1.clone(), - (SecurityMode::PreQuantum, EncryptionStatus::EncryptDecrypt), + (EncryptionHint::Classic, EncryptionStatus::EncryptDecrypt), ), ( coordinate_2.clone(), - (SecurityMode::PreQuantum, EncryptionStatus::EncryptDecrypt), + (EncryptionHint::Classic, EncryptionStatus::EncryptDecrypt), ), ]), ) @@ -240,11 +242,11 @@ fn test_integrity_check() { HashMap::from_iter([ ( coordinate_1.clone(), - (SecurityMode::PreQuantum, EncryptionStatus::EncryptDecrypt), + (EncryptionHint::Classic, EncryptionStatus::EncryptDecrypt), ), ( coordinate_2.clone(), - (SecurityMode::PreQuantum, EncryptionStatus::EncryptDecrypt), + (EncryptionHint::Classic, EncryptionStatus::EncryptDecrypt), ), ]), ) @@ -308,7 +310,7 @@ fn test_covercrypt_kem() { .generate_user_secret_key(&mut msk, &ap) .expect("cannot generate usk"); let (secret, enc) = cc.encaps(&mpk, &ap).unwrap(); - assert_eq!(enc.security_mode(), SecurityMode::PreQuantum); + assert_eq!(enc.security_mode(), EncryptionHint::Classic); let res = cc.decaps(&usk, &enc).unwrap(); assert_eq!(secret, res.unwrap()); @@ -321,7 +323,7 @@ fn test_covercrypt_kem() { .generate_user_secret_key(&mut msk, &ap) .expect("cannot generate usk"); let (secret, enc) = cc.encaps(&mpk, &ap).unwrap(); - assert_eq!(enc.security_mode(), SecurityMode::PostQuantum); + assert_eq!(enc.security_mode(), EncryptionHint::PostQuantum); let res = cc.decaps(&usk, &enc).unwrap(); assert_eq!(secret, res.unwrap()); @@ -334,7 +336,7 @@ fn test_covercrypt_kem() { .generate_user_secret_key(&mut msk, &ap) .expect("cannot generate usk"); let (secret, enc) = cc.encaps(&mpk, &ap).unwrap(); - assert_eq!(enc.security_mode(), SecurityMode::Hybridized); + assert_eq!(enc.security_mode(), EncryptionHint::Hybridized); let res = cc.decaps(&usk, &enc).unwrap(); assert_eq!(secret, res.unwrap()); } @@ -347,12 +349,22 @@ fn test_covercrypt_pke() { let ptx = "testing encryption/decryption".as_bytes(); - let ctx = PkeAc::<{ Aes256Gcm::KEY_LENGTH }, Aes256Gcm>::encrypt(&cc, &mpk, &ap, ptx) - .expect("cannot encrypt!"); + let ctx = PkeAc::< + { Aes256Gcm::KEY_LENGTH }, + { Aes256Gcm::NONCE_LENGTH }, + { Aes256Gcm::TAG_LENGTH }, + Aes256Gcm, + >::encrypt(&cc, &mpk, &ap, ptx) + .expect("cannot encrypt!"); let usk = cc .generate_user_secret_key(&mut msk, &ap) .expect("cannot generate usk"); - let ptx1 = PkeAc::<{ Aes256Gcm::KEY_LENGTH }, Aes256Gcm>::decrypt(&cc, &usk, &ctx) - .expect("cannot decrypt the ciphertext"); + let ptx1 = PkeAc::< + { Aes256Gcm::KEY_LENGTH }, + { Aes256Gcm::NONCE_LENGTH }, + { Aes256Gcm::TAG_LENGTH }, + Aes256Gcm, + >::decrypt(&cc, &usk, &ctx) + .expect("cannot decrypt the ciphertext"); assert_eq!(ptx, &*ptx1.unwrap()); } diff --git a/src/encrypted_header.rs b/src/abe/encrypted_header.rs similarity index 98% rename from src/encrypted_header.rs rename to src/abe/encrypted_header.rs index 4afc1ce5..0e7f326c 100644 --- a/src/encrypted_header.rs +++ b/src/abe/encrypted_header.rs @@ -4,8 +4,12 @@ use cosmian_crypto_core::{ }; use crate::{ - abe_policy::AccessPolicy, api::Covercrypt, core::SHARED_SECRET_LENGTH, traits::KemAc, Error, - MasterPublicKey, UserSecretKey, XEnc, + abe::{ + core::{XEnc, SHARED_SECRET_LENGTH}, + traits::KemAc, + AccessPolicy, Covercrypt, MasterPublicKey, UserSecretKey, + }, + Error, }; /// Encrypted header holding a `Covercrypt` encapsulation of a 256-byte secret, and metadata diff --git a/src/abe/error.rs b/src/abe/error.rs new file mode 100644 index 00000000..191b08c9 --- /dev/null +++ b/src/abe/error.rs @@ -0,0 +1,56 @@ +//! Error type for the crate. + +use core::{fmt::Display, num::TryFromIntError}; + +use cosmian_crypto_core::CryptoCoreError; + +#[derive(Debug)] +pub enum Error { + Kem(String), + CryptoCoreError(CryptoCoreError), + KeyError(String), + AttributeNotFound(String), + ExistingDimension(String), + OperationNotPermitted(String), + InvalidBooleanExpression(String), + InvalidAttribute(String), + DimensionNotFound(String), + ConversionFailed(String), + Tracing(String), +} + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Kem(err) => write!(f, "Kyber error: {err}"), + Self::CryptoCoreError(err) => write!(f, "CryptoCore error{err}"), + Self::KeyError(err) => write!(f, "{err}"), + Self::AttributeNotFound(err) => write!(f, "attribute not found: {err}"), + Self::ExistingDimension(dimension) => { + write!(f, "dimension {dimension} already exists") + } + Self::InvalidBooleanExpression(expr_str) => { + write!(f, "invalid boolean expression: {expr_str}") + } + Self::InvalidAttribute(attr) => write!(f, "invalid attribute: {attr}"), + Self::DimensionNotFound(dim_str) => write!(f, "cannot find dimension: {dim_str}"), + Self::ConversionFailed(err) => write!(f, "Conversion failed: {err}"), + Self::OperationNotPermitted(err) => write!(f, "Operation not permitted: {err}"), + Self::Tracing(err) => write!(f, "tracing error: {err}"), + } + } +} + +impl From for Error { + fn from(e: TryFromIntError) -> Self { + Self::ConversionFailed(e.to_string()) + } +} + +impl From for Error { + fn from(e: CryptoCoreError) -> Self { + Self::CryptoCoreError(e) + } +} + +impl std::error::Error for Error {} diff --git a/src/abe_policy/mod.rs b/src/abe/policy.rs similarity index 93% rename from src/abe_policy/mod.rs rename to src/abe/policy.rs index a54e23b9..9f29ad8c 100644 --- a/src/abe_policy/mod.rs +++ b/src/abe/policy.rs @@ -9,14 +9,15 @@ mod tests; pub use access_policy::AccessPolicy; pub use access_structure::AccessStructure; -pub use attribute::{EncryptionStatus, QualifiedAttribute, SecurityMode}; -use cosmian_crypto_core::bytes_ser_de::Serializable; +pub use attribute::{EncryptionHint, EncryptionStatus, QualifiedAttribute}; pub use dimension::{Attribute, Dimension}; pub use rights::Right; + #[cfg(any(test, feature = "test-utils"))] pub use tests::gen_structure; use crate::Error; +use cosmian_crypto_core::bytes_ser_de::Serializable; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum Version { diff --git a/src/abe_policy/access_policy.rs b/src/abe/policy/access_policy.rs similarity index 78% rename from src/abe_policy/access_policy.rs rename to src/abe/policy/access_policy.rs index 4d85acad..b8ca195d 100644 --- a/src/abe_policy/access_policy.rs +++ b/src/abe/policy/access_policy.rs @@ -7,10 +7,12 @@ use std::{ collections::LinkedList, fmt::Debug, - ops::{BitAnd, BitOr}, + ops::{BitAnd, BitOr, Deref}, }; -use crate::{abe_policy::QualifiedAttribute, Error}; +use cosmian_crypto_core::bytes_ser_de::Serializable; + +use crate::{abe::policy::QualifiedAttribute, Error}; /// An access policy is a boolean expression of qualified attributes. #[derive(Debug, Clone, PartialEq, Eq)] @@ -216,8 +218,63 @@ impl BitOr for AccessPolicy { } } +impl Serializable for AccessPolicy { + type Error = Error; + + fn length(&self) -> usize { + match self { + AccessPolicy::Broadcast => 1, + AccessPolicy::Term(qualified_attribute) => 1 + qualified_attribute.length(), + AccessPolicy::Conjunction(access_policy, access_policy1) => { + 1 + access_policy.length() + access_policy1.length() + } + AccessPolicy::Disjunction(access_policy, access_policy1) => { + 1 + access_policy.length() + access_policy1.length() + } + } + } + + fn write( + &self, + ser: &mut cosmian_crypto_core::bytes_ser_de::Serializer, + ) -> Result { + match self { + AccessPolicy::Broadcast => ser.write(&0_u64).map_err(Error::from), + AccessPolicy::Term(qualified_attribute) => { + Ok(ser.write(&1_u64)? + ser.write(qualified_attribute)?) + } + AccessPolicy::Conjunction(access_policy, access_policy1) => Ok(ser.write(&2_u64)? + + ser.write(access_policy.deref())? + + ser.write(access_policy1.deref())?), + AccessPolicy::Disjunction(access_policy, access_policy1) => Ok(ser.write(&3_u64)? + + ser.write(access_policy.deref())? + + ser.write(access_policy1.deref())?), + } + } + + fn read(de: &mut cosmian_crypto_core::bytes_ser_de::Deserializer) -> Result { + match de.read::()? { + 0 => Ok(Self::Broadcast), + 1 => Ok(Self::Term(de.read()?)), + 2 => Ok(Self::Conjunction( + Box::new(de.read()?), + Box::new(de.read()?), + )), + 3 => Ok(Self::Disjunction( + Box::new(de.read()?), + Box::new(de.read()?), + )), + n => Err(Error::ConversionFailed(format!( + "{n} is not a valid access policy tag" + ))), + } + } +} + #[cfg(test)] mod tests { + use cosmian_crypto_core::bytes_ser_de::test_serialization; + use super::AccessPolicy; #[test] @@ -225,13 +282,20 @@ mod tests { // These are valid access policies. let ap = AccessPolicy::parse("(D1::A && (D2::A) || D2::B)").unwrap(); println!("{ap:#?}"); + test_serialization(&ap).unwrap(); let ap = AccessPolicy::parse("D1::A && D2::A || D2::B").unwrap(); println!("{ap:#?}"); + test_serialization(&ap).unwrap(); let ap = AccessPolicy::parse("D1::A && (D2::A || D2::B)").unwrap(); println!("{ap:#?}"); + test_serialization(&ap).unwrap(); let ap = AccessPolicy::parse("D1::A (D2::A || D2::B)").unwrap(); println!("{ap:#?}"); - assert_eq!(AccessPolicy::parse("*").unwrap(), AccessPolicy::Broadcast); + test_serialization(&ap).unwrap(); + let ap = AccessPolicy::parse("*").unwrap(); + test_serialization(&ap).unwrap(); + assert_eq!(ap, AccessPolicy::Broadcast); + assert!(AccessPolicy::parse("").is_err()); // These are invalid access policies. diff --git a/src/abe_policy/access_structure.rs b/src/abe/policy/access_structure.rs similarity index 97% rename from src/abe_policy/access_structure.rs rename to src/abe/policy/access_structure.rs index b206eafe..50ac8859 100644 --- a/src/abe_policy/access_structure.rs +++ b/src/abe/policy/access_structure.rs @@ -1,8 +1,8 @@ use std::collections::{hash_map::Entry, HashMap, HashSet}; use crate::{ - abe_policy::{ - attribute::SecurityMode, AccessPolicy, Attribute, Dimension, EncryptionStatus, + abe::policy::{ + attribute::EncryptionHint, AccessPolicy, Attribute, Dimension, EncryptionStatus, QualifiedAttribute, Right, Version, }, data_struct::Dict, @@ -102,7 +102,7 @@ impl AccessStructure { pub fn add_attribute( &mut self, attribute: QualifiedAttribute, - security_mode: SecurityMode, + security_mode: EncryptionHint, after: Option<&str>, ) -> Result<(), Error> { let cnt = self @@ -170,7 +170,9 @@ impl AccessStructure { /// Generates all rights defined by this access structure and return their /// hybridization and activation status. - pub(crate) fn omega(&self) -> Result, Error> { + pub(crate) fn omega( + &self, + ) -> Result, Error> { let universe = self.dimensions.iter().collect::>(); combine(universe.as_slice()) .into_iter() @@ -318,11 +320,11 @@ impl AccessStructure { /// - D2::B2 fn combine( dimensions: &[(&String, &Dimension)], -) -> Vec<(Vec, SecurityMode, EncryptionStatus)> { +) -> Vec<(Vec, EncryptionHint, EncryptionStatus)> { if dimensions.is_empty() { vec![( vec![], - SecurityMode::PreQuantum, + EncryptionHint::Classic, EncryptionStatus::EncryptDecrypt, )] } else { @@ -377,7 +379,7 @@ mod serialization { #[test] fn test_access_structure_serialization() { - use crate::abe_policy::gen_structure; + use crate::abe::gen_structure; use cosmian_crypto_core::bytes_ser_de::test_serialization; let mut structure = AccessStructure::new(); @@ -389,7 +391,7 @@ mod serialization { #[cfg(test)] mod tests { use super::*; - use crate::abe_policy::gen_structure; + use crate::abe::gen_structure; #[test] fn test_combine() { @@ -408,9 +410,9 @@ mod tests { structure.add_anarchy("Country".to_string()).unwrap(); [ - ("France", SecurityMode::PreQuantum), - ("Germany", SecurityMode::PreQuantum), - ("Spain", SecurityMode::PreQuantum), + ("France", EncryptionHint::Classic), + ("Germany", EncryptionHint::Classic), + ("Spain", EncryptionHint::Classic), ] .into_iter() .try_for_each(|(attribute, mode)| { diff --git a/src/abe_policy/attribute.rs b/src/abe/policy/attribute.rs similarity index 83% rename from src/abe_policy/attribute.rs rename to src/abe/policy/attribute.rs index 6f8740d2..ec394fef 100644 --- a/src/abe_policy/attribute.rs +++ b/src/abe/policy/attribute.rs @@ -1,40 +1,37 @@ use std::{convert::TryFrom, fmt::Debug, ops::BitOr}; -use cosmian_crypto_core::bytes_ser_de::Serializable; +use cosmian_crypto_core::bytes_ser_de::{Deserializer, Serializable, Serializer}; use serde::{Deserialize, Serialize}; use crate::Error; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub enum SecurityMode { - PreQuantum, +pub enum EncryptionHint { + Classic, PostQuantum, Hybridized, } -impl Serializable for SecurityMode { +impl Serializable for EncryptionHint { type Error = Error; fn length(&self) -> usize { 1 } - fn write( - &self, - ser: &mut cosmian_crypto_core::bytes_ser_de::Serializer, - ) -> Result { + fn write(&self, ser: &mut Serializer) -> Result { match self { - Self::PreQuantum => ser.write(&0usize), + Self::Classic => ser.write(&0usize), Self::Hybridized => ser.write(&1usize), Self::PostQuantum => ser.write(&2usize), } .map_err(Error::from) } - fn read(de: &mut cosmian_crypto_core::bytes_ser_de::Deserializer) -> Result { + fn read(de: &mut Deserializer) -> Result { let status = de.read::()?; match status { - 0 => Ok(Self::PreQuantum), + 0 => Ok(Self::Classic), 1 => Ok(Self::Hybridized), 2 => Ok(Self::PostQuantum), n => Err(Error::ConversionFailed(format!( @@ -79,10 +76,7 @@ impl Serializable for EncryptionStatus { 1 } - fn write( - &self, - ser: &mut cosmian_crypto_core::bytes_ser_de::Serializer, - ) -> Result { + fn write(&self, ser: &mut Serializer) -> Result { match self { Self::DecryptOnly => ser.write(&0usize), Self::EncryptDecrypt => ser.write(&1usize), @@ -90,7 +84,7 @@ impl Serializable for EncryptionStatus { .map_err(Error::from) } - fn read(de: &mut cosmian_crypto_core::bytes_ser_de::Deserializer) -> Result { + fn read(de: &mut Deserializer) -> Result { let status = de.read::()?; match status { 0 => Ok(Self::DecryptOnly), @@ -189,14 +183,11 @@ impl Serializable for QualifiedAttribute { self.dimension.length() + self.name.length() } - fn write( - &self, - ser: &mut cosmian_crypto_core::bytes_ser_de::Serializer, - ) -> Result { + fn write(&self, ser: &mut Serializer) -> Result { Ok(ser.write(&self.dimension)? + ser.write(&self.name)?) } - fn read(de: &mut cosmian_crypto_core::bytes_ser_de::Deserializer) -> Result { + fn read(de: &mut Deserializer) -> Result { Ok(Self { dimension: de.read()?, name: de.read()?, diff --git a/src/abe_policy/dimension.rs b/src/abe/policy/dimension.rs similarity index 92% rename from src/abe_policy/dimension.rs rename to src/abe/policy/dimension.rs index a0d81e52..19c91b8c 100644 --- a/src/abe_policy/dimension.rs +++ b/src/abe/policy/dimension.rs @@ -5,7 +5,7 @@ use std::{ use serde::{Deserialize, Serialize}; -use super::{EncryptionStatus, SecurityMode}; +use super::{EncryptionHint, EncryptionStatus}; use crate::{data_struct::Dict, Error}; type Name = String; @@ -13,12 +13,12 @@ type Name = String; #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] pub struct Attribute { pub(crate) id: usize, - pub(crate) security_mode: SecurityMode, + pub(crate) security_mode: EncryptionHint, pub(crate) encryption_status: EncryptionStatus, } impl Attribute { - pub fn new(security_mode: SecurityMode, id: usize) -> Self { + pub fn new(security_mode: EncryptionHint, id: usize) -> Self { Self { id, security_mode, @@ -30,7 +30,7 @@ impl Attribute { self.id } - pub fn get_security_mode(&self) -> SecurityMode { + pub fn get_security_mode(&self) -> EncryptionHint { self.security_mode } @@ -106,7 +106,7 @@ impl Dimension { pub fn add_attribute( &mut self, attribute: Name, - security_mode: SecurityMode, + security_mode: EncryptionHint, after: Option<&str>, id: usize, ) -> Result<(), Error> { @@ -267,10 +267,10 @@ mod serialization { fn test_attribute_serialization() { use cosmian_crypto_core::bytes_ser_de::test_serialization; - let attribute = Attribute::new(SecurityMode::PreQuantum, 13); + let attribute = Attribute::new(EncryptionHint::Classic, 13); test_serialization(&attribute).unwrap(); - let attribute = Attribute::new(SecurityMode::Hybridized, usize::MAX); + let attribute = Attribute::new(EncryptionHint::Hybridized, usize::MAX); test_serialization(&attribute).unwrap(); } @@ -318,20 +318,20 @@ mod serialization { use cosmian_crypto_core::bytes_ser_de::test_serialization; let mut d = Dimension::Hierarchy(Dict::new()); - d.add_attribute("A".to_string(), SecurityMode::PreQuantum, None, 0) + d.add_attribute("A".to_string(), EncryptionHint::Classic, None, 0) .unwrap(); - d.add_attribute("B".to_string(), SecurityMode::Hybridized, Some("A"), 1) + d.add_attribute("B".to_string(), EncryptionHint::Hybridized, Some("A"), 1) .unwrap(); - d.add_attribute("C".to_string(), SecurityMode::Hybridized, Some("B"), 2) + d.add_attribute("C".to_string(), EncryptionHint::Hybridized, Some("B"), 2) .unwrap(); test_serialization(&d).unwrap(); let mut d = Dimension::Anarchy(HashMap::new()); - d.add_attribute("A".to_string(), SecurityMode::PreQuantum, None, 0) + d.add_attribute("A".to_string(), EncryptionHint::Classic, None, 0) .unwrap(); - d.add_attribute("B".to_string(), SecurityMode::Hybridized, None, 1) + d.add_attribute("B".to_string(), EncryptionHint::Hybridized, None, 1) .unwrap(); - d.add_attribute("C".to_string(), SecurityMode::Hybridized, None, 2) + d.add_attribute("C".to_string(), EncryptionHint::Hybridized, None, 2) .unwrap(); test_serialization(&d).unwrap(); } diff --git a/src/abe_policy/rights.rs b/src/abe/policy/rights.rs similarity index 100% rename from src/abe_policy/rights.rs rename to src/abe/policy/rights.rs diff --git a/src/abe_policy/tests.rs b/src/abe/policy/tests.rs similarity index 86% rename from src/abe_policy/tests.rs rename to src/abe/policy/tests.rs index 02289a57..8467e3d8 100644 --- a/src/abe_policy/tests.rs +++ b/src/abe/policy/tests.rs @@ -1,45 +1,48 @@ -use crate::{abe_policy::AccessStructure, Error, SecurityMode}; +use crate::{ + abe::policy::{AccessStructure, EncryptionHint}, + Error, +}; pub fn gen_structure(policy: &mut AccessStructure, complete: bool) -> Result<(), Error> { policy.add_hierarchy("SEC".to_string())?; policy.add_attribute( - crate::abe_policy::QualifiedAttribute { + crate::abe::policy::QualifiedAttribute { dimension: "SEC".to_string(), name: "LOW".to_string(), }, - SecurityMode::PreQuantum, + EncryptionHint::Classic, None, )?; policy.add_attribute( - crate::abe_policy::QualifiedAttribute { + crate::abe::policy::QualifiedAttribute { dimension: "SEC".to_string(), name: "MED".to_string(), }, - SecurityMode::PostQuantum, + EncryptionHint::PostQuantum, Some("LOW"), )?; policy.add_attribute( - crate::abe_policy::QualifiedAttribute { + crate::abe::policy::QualifiedAttribute { dimension: "SEC".to_string(), name: "TOP".to_string(), }, - SecurityMode::Hybridized, + EncryptionHint::Hybridized, Some("MED"), )?; policy.add_anarchy("DPT".to_string())?; [ - ("RD", SecurityMode::PreQuantum), - ("HR", SecurityMode::PreQuantum), - ("MKG", SecurityMode::PreQuantum), - ("FIN", SecurityMode::PreQuantum), - ("DEV", SecurityMode::PreQuantum), + ("RD", EncryptionHint::Classic), + ("HR", EncryptionHint::Classic), + ("MKG", EncryptionHint::Classic), + ("FIN", EncryptionHint::Classic), + ("DEV", EncryptionHint::Classic), ] .into_iter() .try_for_each(|(attribute, mode)| { policy.add_attribute( - crate::abe_policy::QualifiedAttribute { + crate::abe::policy::QualifiedAttribute { dimension: "DPT".to_string(), name: attribute.to_string(), }, @@ -51,16 +54,16 @@ pub fn gen_structure(policy: &mut AccessStructure, complete: bool) -> Result<(), if complete { policy.add_anarchy("CTR".to_string())?; [ - ("EN", SecurityMode::PreQuantum), - ("DE", SecurityMode::PreQuantum), - ("IT", SecurityMode::PreQuantum), - ("FR", SecurityMode::PreQuantum), - ("SP", SecurityMode::PreQuantum), + ("EN", EncryptionHint::Classic), + ("DE", EncryptionHint::Classic), + ("IT", EncryptionHint::Classic), + ("FR", EncryptionHint::Classic), + ("SP", EncryptionHint::Classic), ] .into_iter() .try_for_each(|(attribute, mode)| { policy.add_attribute( - crate::abe_policy::QualifiedAttribute { + crate::abe::policy::QualifiedAttribute { dimension: "CTR".to_string(), name: attribute.to_string(), }, @@ -106,20 +109,20 @@ fn test_edit_anarchic_attributes() { // Add new attribute Sales let new_attr = QualifiedAttribute::new("DPT", "Sales"); assert!(structure - .add_attribute(new_attr.clone(), SecurityMode::PreQuantum, None) + .add_attribute(new_attr.clone(), EncryptionHint::Classic, None) .is_ok()); assert_eq!(structure.attributes().count(), 9); // Try adding already existing attribute HR let duplicate_attr = QualifiedAttribute::new("DPT", "HR"); assert!(structure - .add_attribute(duplicate_attr, SecurityMode::PreQuantum, None) + .add_attribute(duplicate_attr, EncryptionHint::Classic, None) .is_err()); // Try adding attribute to non existing dimension let missing_dimension = QualifiedAttribute::new("Missing", "dimension"); assert!(structure - .add_attribute(missing_dimension.clone(), SecurityMode::PreQuantum, None) + .add_attribute(missing_dimension.clone(), EncryptionHint::Classic, None) .is_err()); // Remove research attribute @@ -151,14 +154,14 @@ fn test_edit_anarchic_attributes() { structure .add_attribute( QualifiedAttribute::new("DimensionTest", "Attr1"), - SecurityMode::PreQuantum, + EncryptionHint::Classic, None, ) .unwrap(); structure .add_attribute( QualifiedAttribute::new("DimensionTest", "Attr2"), - SecurityMode::PreQuantum, + EncryptionHint::Classic, None, ) .unwrap(); @@ -217,7 +220,7 @@ fn test_edit_hierarchic_attributes() { structure .add_attribute( QualifiedAttribute::new("SEC", "OTHER"), - SecurityMode::PreQuantum, + EncryptionHint::Classic, None, ) .unwrap(); @@ -246,7 +249,7 @@ fn test_edit_hierarchic_attributes() { structure .add_attribute( QualifiedAttribute::new("SEC", "LOW"), - SecurityMode::PreQuantum, + EncryptionHint::Classic, None, ) .unwrap(); @@ -283,7 +286,7 @@ fn test_edit_hierarchic_attributes() { structure .add_attribute( QualifiedAttribute::new("SEC", "MID"), - SecurityMode::PreQuantum, + EncryptionHint::Classic, Some("LOW"), ) .unwrap(); diff --git a/src/abe/traits.rs b/src/abe/traits.rs new file mode 100644 index 00000000..2b7ef1c4 --- /dev/null +++ b/src/abe/traits.rs @@ -0,0 +1,66 @@ +use crate::AccessPolicy; +use cosmian_crypto_core::{traits::AE, Secret}; + +pub trait KemAc { + type EncapsulationKey; + type DecapsulationKey; + type Encapsulation; + type Error: std::error::Error; + + /// Generates a new encapsulation for the given access policy. + /// + /// # Error + /// + /// Returns an error if the access policy is not valid. + fn encaps( + &self, + ek: &Self::EncapsulationKey, + ap: &AccessPolicy, + ) -> Result<(Secret, Self::Encapsulation), Self::Error>; + + /// Attempts opening the given encapsulation with the given key. + /// + /// Returns the encapsulated secret upon success or `None` if this key was + /// not authorized to open this encapsulation. + fn decaps( + &self, + dk: &Self::DecapsulationKey, + enc: &Self::Encapsulation, + ) -> Result>, Self::Error>; +} + +pub trait PkeAc< + const KEY_LENGTH: usize, + const NONCE_LENGTH: usize, + const TAG_LENGTH: usize, + E: AE, +> +{ + type EncryptionKey; + type DecryptionKey; + type Ciphertext; + + type Error: std::error::Error; + + /// Encrypts the given plaintext under the given access policy. + /// + /// # Error + /// + /// Returns an error if the access policy is not valid. + fn encrypt( + &self, + ek: &Self::EncryptionKey, + ap: &AccessPolicy, + ptx: &[u8], + ) -> Result; + + /// Attempts decrypting the given ciphertext with the given key. + /// + /// Returns the plaintext upon success, or `None` if this key was not + /// authorized to decrypt this ciphertext. + fn decrypt( + &self, + dk: &Self::DecryptionKey, + ctx: &Self::Ciphertext, + ) -> Result, Self::Error>; +} diff --git a/src/ae.rs b/src/ae.rs deleted file mode 100644 index 90c488c1..00000000 --- a/src/ae.rs +++ /dev/null @@ -1,36 +0,0 @@ -use cosmian_crypto_core::{ - reexport::{rand_core::CryptoRngCore, zeroize::Zeroizing}, - Aes256Gcm, Dem, FixedSizeCBytes, Instantiable, Nonce, RandomFixedSizeCBytes, SymmetricKey, -}; - -use crate::{traits::AE, Error}; - -impl AE<{ Self::KEY_LENGTH }> for Aes256Gcm { - type Error = Error; - - fn encrypt( - rng: &mut impl CryptoRngCore, - key: &SymmetricKey<{ Self::KEY_LENGTH }>, - ptx: &[u8], - ) -> Result, Error> { - let nonce = Nonce::<{ Self::NONCE_LENGTH }>::new(&mut *rng); - let ciphertext = Self::new(key).encrypt(&nonce, ptx, None)?; - Ok([nonce.as_bytes(), &ciphertext].concat()) - } - - fn decrypt( - key: &SymmetricKey<{ Self::KEY_LENGTH }>, - ctx: &[u8], - ) -> Result>, Error> { - if ctx.len() < Self::NONCE_LENGTH { - return Err(Error::CryptoCoreError( - cosmian_crypto_core::CryptoCoreError::DecryptionError, - )); - } - let nonce = Nonce::try_from_slice(&ctx[..Self::NONCE_LENGTH])?; - Self::new(key) - .decrypt(&nonce, &ctx[Self::NONCE_LENGTH..], None) - .map_err(Error::CryptoCoreError) - .map(Zeroizing::new) - } -} diff --git a/src/base.rs b/src/base.rs new file mode 100644 index 00000000..dd9acca9 --- /dev/null +++ b/src/base.rs @@ -0,0 +1,367 @@ +use crate::{ + abe::{traits::KemAc, AccessPolicy}, + providers::{MlKem, PreQuantumKem}, + AccessStructure, Covercrypt, Error, MasterPublicKey, MasterSecretKey, UserSecretKey, XEnc, +}; +use cosmian_crypto_core::{ + bytes_ser_de::Serializable, + kdf::Hasher, + reexport::{rand_core::CryptoRngCore, tiny_keccak::Sha3, zeroize::ZeroizeOnDrop}, + traits::{kem_to_pke::GenericPKE, KEM}, + Secret, SymmetricKey, +}; + +#[derive(Debug)] +pub enum AbeDKey { + Master(Covercrypt, MasterSecretKey), + User(Covercrypt, UserSecretKey), +} + +// All secret keys are zeroized on drop. +impl ZeroizeOnDrop for AbeDKey {} + +#[derive(Debug, ZeroizeOnDrop)] +pub enum DKey { + AbeScheme(AbeDKey), + PreQuantum(>::DecapsulationKey), + PostQuantum(>::DecapsulationKey), + Hybridized( + >::DecapsulationKey, + >::DecapsulationKey, + ), +} + +impl DKey { + pub fn access_structure(&mut self) -> Result<&mut AccessStructure, Error> { + match self { + DKey::AbeScheme(AbeDKey::Master(_, msk)) => Ok(&mut msk.access_structure), + _ => Err(Error::KeyError( + "no access structure associated to non-ABE key type".to_string(), + )), + } + } + + pub fn update_msk(&mut self) -> Result { + match self { + DKey::AbeScheme(AbeDKey::Master(cc, msk)) => { + let mpk = cc.update_msk(msk)?; + Ok(EKey::AbeScheme(cc.clone(), mpk, None)) + } + _ => Err(Error::KeyError( + "cannot update non ABE master key".to_string(), + )), + } + } + + pub fn rekey(&mut self, ap: &AccessPolicy) -> Result { + match self { + DKey::AbeScheme(AbeDKey::Master(cc, msk)) => { + let mpk = cc.rekey(msk, ap)?; + Ok(EKey::AbeScheme(cc.clone(), mpk, None)) + } + _ => Err(Error::KeyError( + "cannot re-key non ABE master key".to_string(), + )), + } + } + + pub fn prune_master_key(&mut self, ap: &AccessPolicy) -> Result { + match self { + DKey::AbeScheme(AbeDKey::Master(cc, msk)) => { + let mpk = cc.prune_master_secret_key(msk, ap)?; + Ok(EKey::AbeScheme(cc.clone(), mpk, None)) + } + _ => Err(Error::KeyError( + "cannot prune non ABE master key".to_string(), + )), + } + } + + pub fn generate_user_secret_key(&mut self, ap: &AccessPolicy) -> Result { + match self { + DKey::AbeScheme(AbeDKey::Master(cc, msk)) => { + let usk = cc.generate_user_secret_key(msk, ap)?; + Ok(DKey::AbeScheme(AbeDKey::User(cc.clone(), usk))) + } + _ => Err(Error::KeyError( + "cannot generate user secret key using a non ABE master key".to_string(), + )), + } + } + + pub fn refresh_user_secret_key( + &mut self, + usk: &mut DKey, + keep_old_secrets: bool, + ) -> Result<(), Error> { + match (self, usk) { + (DKey::AbeScheme(AbeDKey::Master(cc, msk)), DKey::AbeScheme(AbeDKey::User(_, usk))) => { + cc.refresh_usk(msk, usk, keep_old_secrets) + } + _ => Err(Error::KeyError( + "cannot refresh user secret key: invalid key types".to_string(), + )), + } + } + + pub fn recaps( + &mut self, + mpk: &EKey, + enc: &Enc, + ) -> Result<(SymmetricKey<{ ConfigurableKEM::KEY_LENGTH }>, Enc), Error> { + match (self, mpk, enc) { + ( + DKey::AbeScheme(AbeDKey::Master(cc, msk)), + EKey::AbeScheme(_, mpk, _), + Enc::AbeScheme(enc), + ) => { + let (ss, enc) = cc.recaps(msk, mpk, enc)?; + Ok((SymmetricKey::from(ss), Enc::AbeScheme(enc))) + } + _ => Err(Error::KeyError( + "cannot re-encapsulate: invalid object types".to_string(), + )), + } + } +} + +#[derive(Debug, Clone)] +pub enum EKey { + AbeScheme(Covercrypt, MasterPublicKey, Option), + PreQuantum(>::EncapsulationKey), + PostQuantum(>::EncapsulationKey), + Hybridized( + >::EncapsulationKey, + >::EncapsulationKey, + ), +} + +impl EKey { + /// Sets the encapsulation key to use the provided access polity. + pub fn set_access_policy(&mut self, access_policy: AccessPolicy) -> Result<(), Error> { + match self { + Self::AbeScheme(_, _, ap) => { + *ap = Some(access_policy); + Ok(()) + } + _ => Err(Error::KeyError( + "cannot set access policy for non-ABE encapsulation keys".to_string(), + )), + } + } +} + +#[derive(Debug, Clone)] +pub enum Enc { + AbeScheme(XEnc), + PreQuantum(>::Encapsulation), + PostQuantum(>::Encapsulation), + Hybridized( + >::Encapsulation, + >::Encapsulation, + ), +} + +#[derive(Debug, Clone)] +pub enum Configuration { + AbeScheme, + PreQuantum, + PostQuantum, + Hybridized, +} + +impl Configuration { + pub fn keygen(&self, rng: &mut impl CryptoRngCore) -> Result<(DKey, EKey), Error> { + match self { + Self::AbeScheme => { + let cc = Covercrypt::default(); + let (msk, mpk) = cc.setup()?; + Ok(( + DKey::AbeScheme(AbeDKey::Master(cc.clone(), msk)), + EKey::AbeScheme(cc, mpk, None), + )) + } + Self::PreQuantum => { + let (dk, ek) = PreQuantumKem::keygen(rng)?; + Ok((DKey::PreQuantum(dk), EKey::PreQuantum(ek))) + } + Self::PostQuantum => { + let (dk, ek) = MlKem::keygen(rng)?; + Ok((DKey::PostQuantum(dk), EKey::PostQuantum(ek))) + } + Self::Hybridized => { + let (pre_dk, pre_ek) = PreQuantumKem::keygen(rng)?; + let (post_dk, post_ek) = MlKem::keygen(rng)?; + Ok(( + DKey::Hybridized(pre_dk, post_dk), + EKey::Hybridized(pre_ek, post_ek), + )) + } + } + } +} + +/// Interface of a CCA KEM that can be configured to be either: +/// - a pre-quantum KEM based on the ElGamal provider; +/// - a post-quantum KEM based on the MlKem provider; +/// - a hybridized KEM based on both the ElGamal and MlKem provider and +/// guaranteeing bast-of-both security; +/// - an ABE KEM based on Covercrypt and in which case the security against a +/// post-quantum adversary is defined on a per-attribute basis similarly to +/// Covercrypt. +/// +/// Note while the constant-time characteristic of these variant depends on the +/// specific provider, the ABE variant *is not* constant-time as Covercrypt is +/// not. +#[derive(Debug, Clone)] +pub struct ConfigurableKEM; + +impl KEM<32> for ConfigurableKEM { + type Encapsulation = Enc; + + type EncapsulationKey = EKey; + + type DecapsulationKey = DKey; + + type Error = Error; + + fn keygen( + _rng: &mut impl CryptoRngCore, + ) -> Result<(Self::DecapsulationKey, Self::EncapsulationKey), Self::Error> { + Err(Error::Kem( + "key generation is not implemented for ConfigurableKEM, use the KEMConfiguration instead" + .to_string(), + )) + } + + fn enc( + ek: &Self::EncapsulationKey, + rng: &mut impl CryptoRngCore, + ) -> Result<(SymmetricKey<32>, Self::Encapsulation), Self::Error> { + match ek { + EKey::AbeScheme(_, _, None) => Err(Error::Kem( + "access policy must be provided for encapsulation".to_string(), + )), + EKey::AbeScheme(cc, mpk, Some(ap)) => cc + .encaps(mpk, ap) + .map(|(key, enc)| (SymmetricKey::from(key), Enc::AbeScheme(enc))), + EKey::PreQuantum(ek) => { + let (key, enc) = + PreQuantumKem::enc(ek, rng).map_err(|e| Error::Kem(e.to_string()))?; + Ok((key, Enc::PreQuantum(enc))) + } + EKey::PostQuantum(ek) => { + let (key, enc) = MlKem::enc(ek, rng).map_err(|e| Error::Kem(e.to_string()))?; + Ok((key, Enc::PostQuantum(enc))) + } + EKey::Hybridized(pre_ek, post_ek) => { + let (k1, enc1) = + PreQuantumKem::enc(pre_ek, rng).map_err(|e| Error::Kem(e.to_string()))?; + let (k2, enc2) = MlKem::enc(post_ek, rng).map_err(|e| Error::Kem(e.to_string()))?; + let mut key = SymmetricKey::default(); + let mut hasher = Sha3::v256(); + hasher.update(&*k1); + hasher.update(&*k2); + hasher.update(&enc1.serialize()?); + hasher.update(&enc2); + hasher.finalize(&mut *key); + Ok((key, Enc::Hybridized(enc1, enc2))) + } + } + } + + fn dec( + dk: &Self::DecapsulationKey, + enc: &Self::Encapsulation, + ) -> Result, Self::Error> { + match (dk, enc) { + (DKey::AbeScheme(dk), Enc::AbeScheme(xenc)) => match dk { + AbeDKey::Master(_cc, msk) => { + let (ss, _) = crate::abe::core::primitives::master_decaps(msk, xenc, false)?; + Ok(SymmetricKey::from(ss)) + } + AbeDKey::User(cc, usk) => cc.decaps(usk, xenc).map(|res| { + // If the user does not have the right to decapsulate, + // return a random key to preserve the KEM semantics. + let ss = res.unwrap_or_else(|| Secret::random(&mut *cc.rng())); + SymmetricKey::from(ss) + }), + }, + (DKey::PreQuantum(dk), Enc::PreQuantum(enc)) => { + PreQuantumKem::dec(dk, enc).map_err(|e| Error::Kem(e.to_string())) + } + (DKey::PostQuantum(dk), Enc::PostQuantum(enc)) => { + MlKem::dec(dk, enc).map_err(|e| Error::Kem(e.to_string())) + } + (DKey::Hybridized(dk1, dk2), Enc::Hybridized(enc1, enc2)) => { + let k1 = PreQuantumKem::dec(dk1, enc1).map_err(|e| Error::Kem(e.to_string()))?; + let k2 = MlKem::dec(dk2, enc2).map_err(|e| Error::Kem(e.to_string()))?; + let mut key = SymmetricKey::default(); + let mut hasher = Sha3::v256(); + hasher.update(&*k1); + hasher.update(&*k2); + hasher.update(&enc1.serialize()?); + hasher.update(enc2); + hasher.finalize(&mut *key); + Ok(key) + } + _ => Err(Error::KeyError( + "cannot proceed with decapsulation: incompatible types".to_string(), + )), + } + } +} + +pub type ConfigurablePKE = GenericPKE<{ ConfigurableKEM::KEY_LENGTH }, ConfigurableKEM, AE>; + +#[cfg(test)] +mod tests { + use crate::test_utils::cc_keygen; + + use super::*; + use cosmian_crypto_core::{reexport::rand_core::SeedableRng, CsRng}; + + #[test] + fn test_abe_kem() { + let mut rng = CsRng::from_entropy(); + let config = Configuration::AbeScheme; + let (mut msk, _) = config.keygen(&mut rng).unwrap(); + + // Load the test access structure used in other tests. + let access_structure = msk.access_structure().unwrap(); + let (_msk, _) = cc_keygen(&Covercrypt::default(), true).unwrap(); + *access_structure = _msk.access_structure.clone(); + let mut mpk = msk.update_msk().unwrap(); + + let user_ap = AccessPolicy::parse("(DPT::MKG || DPT::FIN) && SEC::TOP").unwrap(); + let ok_ap = AccessPolicy::parse("DPT::MKG && SEC::TOP").unwrap(); + let ko_ap = AccessPolicy::parse("DPT::DEV").unwrap(); + + let usk = msk.generate_user_secret_key(&user_ap).unwrap(); + + // Check user *can* decrypt the OK access policy. + mpk.set_access_policy(ok_ap).unwrap(); + let (key, enc) = ConfigurableKEM::enc(&mpk, &mut rng).unwrap(); + let key_ = ConfigurableKEM::dec(&usk, &enc).unwrap(); + assert_eq!(key, key_); + + // Check user *cannot* decrypt the KO access policy. + mpk.set_access_policy(ko_ap).unwrap(); + let (_key, enc) = ConfigurableKEM::enc(&mpk, &mut rng).unwrap(); + let key_ = ConfigurableKEM::dec(&usk, &enc).unwrap(); + assert!(key != key_); + } + + #[test] + fn test_hybridized_kem() { + let mut rng = CsRng::from_entropy(); + let (sk, pk) = Configuration::Hybridized.keygen(&mut rng).unwrap(); + let (key, enc) = ConfigurableKEM::enc(&pk, &mut rng).unwrap(); + let key_ = ConfigurableKEM::dec(&sk, &enc).unwrap(); + assert_eq!(key, key_); + + let (sk, _) = Configuration::Hybridized.keygen(&mut rng).unwrap(); + let key_ = ConfigurableKEM::dec(&sk, &enc).unwrap(); + assert!(key != key_); + } +} diff --git a/src/core/nike/p256.rs b/src/core/nike/p256.rs deleted file mode 100644 index ac7ca4a8..00000000 --- a/src/core/nike/p256.rs +++ /dev/null @@ -1,434 +0,0 @@ -use std::hash::Hash; -use std::iter::Sum; -use std::ops::Add; -use std::ops::AddAssign; -use std::ops::Div; -use std::ops::Mul; -use std::ops::MulAssign; -use std::ops::Sub; -use std::ops::SubAssign; - -use cosmian_crypto_core::bytes_ser_de::Deserializer; -use cosmian_crypto_core::bytes_ser_de::Serializable; -use cosmian_crypto_core::bytes_ser_de::Serializer; -use cosmian_crypto_core::reexport::tiny_keccak::Hasher; -use cosmian_crypto_core::reexport::tiny_keccak::Sha3; -use cosmian_crypto_core::reexport::zeroize::Zeroize; -use cosmian_crypto_core::CryptoCoreError; -use elliptic_curve::group::GroupEncoding; -use elliptic_curve::rand_core::CryptoRngCore; -use elliptic_curve::Field; -use elliptic_curve::PrimeField; -use p256::{ProjectivePoint, Scalar}; -use subtle::ConstantTimeEq; - -use crate::traits::Group; -use crate::traits::KeyHomomorphicNike; -use crate::traits::Nike; -use crate::traits::One; -use crate::traits::Ring; -use crate::traits::Sampling; -use crate::traits::Zero; -use crate::Error; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct P256Point(ProjectivePoint); - -impl Zero for P256Point { - fn zero() -> Self { - Self(ProjectivePoint::IDENTITY) - } - - fn is_zero(&self) -> bool { - self.0.ct_eq(&ProjectivePoint::IDENTITY).into() - } -} - -impl Zeroize for P256Point { - fn zeroize(&mut self) { - self.0.zeroize() - } -} - -impl Add for P256Point { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - &self + &rhs - } -} - -impl Add<&P256Point> for P256Point { - type Output = Self; - - fn add(self, rhs: &P256Point) -> Self::Output { - &self + rhs - } -} - -impl Add<&P256Point> for &P256Point { - type Output = P256Point; - - fn add(self, rhs: &P256Point) -> Self::Output { - P256Point(self.0 + rhs.0) - } -} - -impl AddAssign for P256Point { - fn add_assign(&mut self, rhs: Self) { - self.0 = self.0 + rhs.0; - } -} - -impl Sub for P256Point { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - &self - &rhs - } -} - -impl SubAssign for P256Point { - fn sub_assign(&mut self, rhs: Self) { - self.0 = self.0 - rhs.0 - } -} - -impl Sub<&P256Point> for P256Point { - type Output = Self; - - fn sub(self, rhs: &P256Point) -> Self::Output { - &self - rhs - } -} - -impl Sub<&P256Point> for &P256Point { - type Output = P256Point; - - fn sub(self, rhs: &P256Point) -> Self::Output { - P256Point(self.0 - rhs.0) - } -} - -impl Group for P256Point {} - -impl Serializable for P256Point { - type Error = CryptoCoreError; - - fn length(&self) -> usize { - 33 - } - - fn write(&self, ser: &mut Serializer) -> Result { - ser.write_array(&self.0.to_bytes()) - } - - fn read(de: &mut Deserializer) -> Result { - let bytes = de.read_array::<33>()?; - let point = ProjectivePoint::from_bytes(&bytes.into()) - .into_option() - .ok_or_else(|| { - CryptoCoreError::GenericDeserializationError("cannot deserialize point".to_string()) - })?; - Ok(Self(point)) - } -} - -impl Sum for P256Point { - fn sum>(iter: I) -> Self { - iter.fold(Self::zero(), |a, p| a + p) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct P256Scalar(Scalar); - -impl Hash for P256Scalar { - fn hash(&self, state: &mut H) { - state.write(&self.0.to_bytes()); - } -} - -impl Zero for P256Scalar { - fn zero() -> Self { - Self(Scalar::ZERO) - } - - fn is_zero(&self) -> bool { - self.0.ct_eq(&Scalar::ZERO).into() - } -} - -impl One for P256Scalar { - fn one() -> Self { - Self(Scalar::ONE) - } - - fn is_one(&self) -> bool { - self.0.ct_eq(&Scalar::ONE).into() - } -} - -impl Add for P256Scalar { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - &self + &rhs - } -} - -impl AddAssign for P256Scalar { - fn add_assign(&mut self, rhs: Self) { - self.0 = self.0 + rhs.0; - } -} - -impl Add<&P256Scalar> for P256Scalar { - type Output = Self; - - fn add(self, rhs: &P256Scalar) -> Self::Output { - &self + rhs - } -} - -impl Add<&P256Scalar> for &P256Scalar { - type Output = P256Scalar; - - fn add(self, rhs: &P256Scalar) -> Self::Output { - P256Scalar(self.0 + rhs.0) - } -} - -impl Sub for P256Scalar { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - &self - &rhs - } -} - -impl SubAssign for P256Scalar { - fn sub_assign(&mut self, rhs: Self) { - self.0 = self.0 - rhs.0 - } -} - -impl Sub<&P256Scalar> for P256Scalar { - type Output = Self; - - fn sub(self, rhs: &P256Scalar) -> Self::Output { - &self - rhs - } -} - -impl Sub<&P256Scalar> for &P256Scalar { - type Output = P256Scalar; - - fn sub(self, rhs: &P256Scalar) -> Self::Output { - P256Scalar(self.0 - rhs.0) - } -} - -impl Mul for P256Scalar { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - &self * &rhs - } -} - -impl MulAssign for P256Scalar { - fn mul_assign(&mut self, rhs: Self) { - self.0 = self.0 * rhs.0 - } -} - -impl Mul<&P256Scalar> for P256Scalar { - type Output = Self; - - fn mul(self, rhs: &P256Scalar) -> Self::Output { - &self * rhs - } -} - -impl Mul<&P256Scalar> for &P256Scalar { - type Output = P256Scalar; - - fn mul(self, rhs: &P256Scalar) -> Self::Output { - P256Scalar(self.0 * rhs.0) - } -} - -impl Div for P256Scalar { - type Output = Result; - - fn div(self, rhs: Self) -> Self::Output { - &self / &rhs - } -} - -impl Div<&P256Scalar> for P256Scalar { - type Output = Result; - - fn div(self, rhs: &P256Scalar) -> Self::Output { - &self / rhs - } -} - -impl Div<&P256Scalar> for &P256Scalar { - type Output = Result; - - fn div(self, rhs: &P256Scalar) -> Self::Output { - rhs.0 - .invert() - .map(|rhs| self.0 * rhs) - .map(P256Scalar) - .into_option() - .ok_or_else(|| Error::OperationNotPermitted("Division by zero".to_string())) - } -} - -impl Sum for P256Scalar { - fn sum>(iter: I) -> Self { - iter.fold(Self::zero(), |a, s| a + s) - } -} - -impl Group for P256Scalar {} - -impl Ring for P256Scalar { - type DivError = Error; -} - -impl Serializable for P256Scalar { - type Error = CryptoCoreError; - - fn length(&self) -> usize { - 32 - } - - fn write(&self, ser: &mut Serializer) -> Result { - ser.write_array(&self.0.to_bytes()) - } - - fn read(de: &mut Deserializer) -> Result { - let bytes = de.read_array::<32>()?; - let scalar = Scalar::from_repr(bytes.into()) - .into_option() - .ok_or_else(|| { - CryptoCoreError::GenericDeserializationError( - "cannot deserialize scalar".to_string(), - ) - })?; - Ok(Self(scalar)) - } -} - -impl Sampling for P256Scalar { - fn random(rng: &mut impl CryptoRngCore) -> Self { - Self(Scalar::random(rng)) - } - - fn hash(seed: &[u8]) -> Self { - let mut i = 0u32; - loop { - let mut hasher = Sha3::v256(); - let mut bytes = [0; 32]; - hasher.update(seed); - hasher.update(&i.to_be_bytes()); - hasher.finalize(&mut bytes); - let s = Self::deserialize(&bytes); - bytes.zeroize(); - if let Ok(s) = s { - return s; - } else { - i += 1; - } - } - } -} - -impl From<&P256Scalar> for P256Point { - fn from(s: &P256Scalar) -> Self { - P256Point(ProjectivePoint::GENERATOR * s.0) - } -} - -impl Mul for P256Point { - type Output = Self; - - fn mul(self, rhs: P256Scalar) -> Self::Output { - &self * &rhs - } -} - -impl MulAssign for P256Point { - fn mul_assign(&mut self, rhs: P256Scalar) { - self.0 = self.0 * rhs.0 - } -} - -impl Mul<&P256Scalar> for P256Point { - type Output = Self; - - fn mul(self, rhs: &P256Scalar) -> Self::Output { - &self * rhs - } -} - -impl Mul<&P256Scalar> for &P256Point { - type Output = P256Point; - - fn mul(self, rhs: &P256Scalar) -> Self::Output { - P256Point(self.0 * rhs.0) - } -} - -pub struct P256; - -impl Nike for P256 { - type SecretKey = P256Scalar; - type PublicKey = P256Point; - type SessionKey = P256Point; - type Error = Error; - - fn keygen( - rng: &mut impl CryptoRngCore, - ) -> Result<(Self::SecretKey, Self::PublicKey), Self::Error> { - let sk = Self::SecretKey::random(rng); - let pk = Self::PublicKey::from(&sk); - Ok((sk, pk)) - } - - fn session_key( - sk: &Self::SecretKey, - pk: &Self::PublicKey, - ) -> Result { - Ok(pk * sk) - } -} - -impl KeyHomomorphicNike for P256 {} - -#[cfg(test)] -mod tests { - use cosmian_crypto_core::{ - bytes_ser_de::test_serialization, reexport::rand_core::SeedableRng, CsRng, - }; - - use super::*; - - #[test] - fn test_p256() { - let mut rng = CsRng::from_entropy(); - let (sk1, pk1) = P256::keygen(&mut rng).unwrap(); - let (sk2, pk2) = P256::keygen(&mut rng).unwrap(); - test_serialization(&sk1).unwrap(); - test_serialization(&pk1).unwrap(); - test_serialization(&sk2).unwrap(); - test_serialization(&pk2).unwrap(); - let ss1 = P256::session_key(&sk1, &pk2).unwrap(); - let ss2 = P256::session_key(&sk2, &pk1).unwrap(); - assert_eq!(ss1, ss2); - } -} diff --git a/src/core/nike/r25519.rs b/src/core/nike/r25519.rs deleted file mode 100644 index be87bbaa..00000000 --- a/src/core/nike/r25519.rs +++ /dev/null @@ -1,403 +0,0 @@ -use std::iter::Sum; -use std::ops::Add; -use std::ops::AddAssign; -use std::ops::Deref; -use std::ops::Div; -use std::ops::Mul; -use std::ops::MulAssign; -use std::ops::Sub; -use std::ops::SubAssign; - -use cosmian_crypto_core::bytes_ser_de::Deserializer; -use cosmian_crypto_core::{ - bytes_ser_de::{Serializable, Serializer}, - reexport::{ - rand_core::CryptoRngCore, - tiny_keccak::{Hasher, Sha3}, - zeroize::Zeroize, - }, - CryptoCoreError, R25519PrivateKey as Scalar, R25519PublicKey as EcPoint, -}; - -use crate::{ - traits::{Group, KeyHomomorphicNike, Nike, One, Ring, Sampling, Zero}, - Error, -}; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct R25519Point(EcPoint); - -impl Zero for R25519Point { - fn zero() -> Self { - Self(EcPoint::identity()) - } - - fn is_zero(&self) -> bool { - self == &Self::zero() - } -} - -// TODO: for some reason, the derive macro cannot be used. -impl Zeroize for R25519Point { - fn zeroize(&mut self) { - self.0.zeroize(); - } -} - -impl Add for R25519Point { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - Self(self.0 + &rhs.0) - } -} - -impl Add<&R25519Point> for R25519Point { - type Output = Self; - - fn add(self, rhs: &R25519Point) -> Self::Output { - Self(self.0 + &rhs.0) - } -} - -impl Add<&R25519Point> for &R25519Point { - type Output = R25519Point; - - fn add(self, rhs: &R25519Point) -> Self::Output { - R25519Point(&self.0 + &rhs.0) - } -} - -impl AddAssign for R25519Point { - fn add_assign(&mut self, rhs: Self) { - self.0 = &self.0 + &rhs.0; - } -} - -impl Sub for R25519Point { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - Self(&self.0 - &rhs.0) - } -} - -impl SubAssign for R25519Point { - fn sub_assign(&mut self, rhs: Self) { - self.0 = &self.0 - &rhs.0 - } -} - -impl Sub<&R25519Point> for R25519Point { - type Output = Self; - - fn sub(self, rhs: &R25519Point) -> Self::Output { - Self(&self.0 - &rhs.0) - } -} - -impl Sub<&R25519Point> for &R25519Point { - type Output = R25519Point; - - fn sub(self, rhs: &R25519Point) -> Self::Output { - R25519Point(&self.0 - &rhs.0) - } -} - -impl Group for R25519Point {} - -impl Serializable for R25519Point { - type Error = Error; - - fn length(&self) -> usize { - self.0.length() - } - - fn write(&self, ser: &mut Serializer) -> Result { - self.0.write(ser).map_err(Self::Error::from) - } - - fn read(de: &mut Deserializer) -> Result { - de.read().map(Self).map_err(Self::Error::from) - } -} - -impl Sum for R25519Point { - fn sum>(iter: I) -> Self { - iter.fold(Self::zero(), |a, p| a + p) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct R25519Scalar(Scalar); - -impl Deref for R25519Scalar { - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - self.0.as_bytes() - } -} - -impl Zero for R25519Scalar { - fn zero() -> Self { - Self(Scalar::zero()) - } - - fn is_zero(&self) -> bool { - self == &Self::zero() - } -} - -impl One for R25519Scalar { - fn one() -> Self { - Self(Scalar::one()) - } - - fn is_one(&self) -> bool { - self == &Self::one() - } -} - -impl Add for R25519Scalar { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - Self(self.0 + rhs.0) - } -} - -impl AddAssign for R25519Scalar { - fn add_assign(&mut self, rhs: Self) { - self.0 = &self.0 + &rhs.0; - } -} - -impl Add<&R25519Scalar> for R25519Scalar { - type Output = Self; - - fn add(self, rhs: &R25519Scalar) -> Self::Output { - Self(&self.0 + &rhs.0) - } -} - -impl Add<&R25519Scalar> for &R25519Scalar { - type Output = R25519Scalar; - - fn add(self, rhs: &R25519Scalar) -> Self::Output { - R25519Scalar(&self.0 + &rhs.0) - } -} - -impl Sub for R25519Scalar { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - Self(&self.0 - &rhs.0) - } -} - -impl SubAssign for R25519Scalar { - fn sub_assign(&mut self, rhs: Self) { - self.0 = &self.0 - &rhs.0 - } -} - -impl Sub<&R25519Scalar> for R25519Scalar { - type Output = Self; - - fn sub(self, rhs: &R25519Scalar) -> Self::Output { - Self(&self.0 - &rhs.0) - } -} - -impl Sub<&R25519Scalar> for &R25519Scalar { - type Output = R25519Scalar; - - fn sub(self, rhs: &R25519Scalar) -> Self::Output { - R25519Scalar(&self.0 - &rhs.0) - } -} - -impl Mul for R25519Scalar { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - Self(&self.0 * &rhs.0) - } -} - -impl MulAssign for R25519Scalar { - fn mul_assign(&mut self, rhs: Self) { - self.0 = &self.0 * &rhs.0 - } -} - -impl Mul<&R25519Scalar> for R25519Scalar { - type Output = Self; - - fn mul(self, rhs: &R25519Scalar) -> Self::Output { - Self(&self.0 * &rhs.0) - } -} - -impl Mul<&R25519Scalar> for &R25519Scalar { - type Output = R25519Scalar; - - fn mul(self, rhs: &R25519Scalar) -> Self::Output { - R25519Scalar(&self.0 * &rhs.0) - } -} - -impl Div for R25519Scalar { - type Output = Result; - - fn div(self, rhs: Self) -> Self::Output { - &self / &rhs - } -} - -impl Div<&R25519Scalar> for R25519Scalar { - type Output = Result; - - fn div(self, rhs: &R25519Scalar) -> Self::Output { - &self / rhs - } -} - -impl Div<&R25519Scalar> for &R25519Scalar { - type Output = Result; - - fn div(self, rhs: &R25519Scalar) -> Self::Output { - (&self.0 / &rhs.0).map(R25519Scalar) - } -} - -impl Sum for R25519Scalar { - fn sum>(iter: I) -> Self { - iter.fold(Self::zero(), |a, s| a + s) - } -} - -impl Group for R25519Scalar {} - -impl Ring for R25519Scalar { - type DivError = CryptoCoreError; -} - -impl Serializable for R25519Scalar { - type Error = Error; - - fn length(&self) -> usize { - self.0.length() - } - - fn write(&self, ser: &mut Serializer) -> Result { - self.0.write(ser).map_err(Self::Error::from) - } - - fn read(de: &mut Deserializer) -> Result { - de.read().map(Self).map_err(Self::Error::from) - } -} - -impl Sampling for R25519Scalar { - fn random(rng: &mut impl CryptoRngCore) -> Self { - Self(Scalar::new(rng)) - } - - fn hash(seed: &[u8]) -> Self { - let mut hasher = Sha3::v512(); - let mut bytes = [0; 512 / 8]; - hasher.update(seed); - hasher.finalize(&mut bytes); - let s = Self(Scalar::from_raw_bytes(&bytes)); - bytes.zeroize(); - s - } -} - -impl From<&R25519Scalar> for R25519Point { - fn from(s: &R25519Scalar) -> Self { - Self(EcPoint::from(&s.0)) - } -} - -impl Mul for R25519Point { - type Output = Self; - - fn mul(self, rhs: R25519Scalar) -> Self::Output { - Self(&self.0 * &rhs.0) - } -} - -impl MulAssign for R25519Point { - fn mul_assign(&mut self, rhs: R25519Scalar) { - self.0 = &self.0 * &rhs.0 - } -} - -impl Mul<&R25519Scalar> for R25519Point { - type Output = Self; - - fn mul(self, rhs: &R25519Scalar) -> Self::Output { - Self(&self.0 * &rhs.0) - } -} - -impl Mul<&R25519Scalar> for &R25519Point { - type Output = R25519Point; - - fn mul(self, rhs: &R25519Scalar) -> Self::Output { - R25519Point(&self.0 * &rhs.0) - } -} - -pub struct R25519; - -impl Nike for R25519 { - type SecretKey = R25519Scalar; - type PublicKey = R25519Point; - type SessionKey = R25519Point; - type Error = Error; - - fn keygen( - rng: &mut impl CryptoRngCore, - ) -> Result<(Self::SecretKey, Self::PublicKey), Self::Error> { - let sk = Self::SecretKey::random(rng); - let pk = Self::PublicKey::from(&sk); - Ok((sk, pk)) - } - - fn session_key( - sk: &Self::SecretKey, - pk: &Self::PublicKey, - ) -> Result { - Ok(pk * sk) - } -} - -impl KeyHomomorphicNike for R25519 {} - -#[cfg(test)] -mod tests { - use cosmian_crypto_core::{ - bytes_ser_de::test_serialization, reexport::rand_core::SeedableRng, CsRng, - }; - - use super::*; - - #[test] - fn test_r25519() { - let mut rng = CsRng::from_entropy(); - let (sk1, pk1) = R25519::keygen(&mut rng).unwrap(); - let (sk2, pk2) = R25519::keygen(&mut rng).unwrap(); - test_serialization(&sk1).unwrap(); - test_serialization(&pk1).unwrap(); - test_serialization(&sk2).unwrap(); - test_serialization(&pk2).unwrap(); - let ss1 = R25519::session_key(&sk1, &pk2).unwrap(); - let ss2 = R25519::session_key(&sk2, &pk1).unwrap(); - assert_eq!(ss1, ss2); - } -} diff --git a/src/error.rs b/src/error.rs index 191b08c9..797244dc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -23,7 +23,7 @@ impl Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Kem(err) => write!(f, "Kyber error: {err}"), - Self::CryptoCoreError(err) => write!(f, "CryptoCore error{err}"), + Self::CryptoCoreError(err) => write!(f, "CryptoCore error: {err}"), Self::KeyError(err) => write!(f, "{err}"), Self::AttributeNotFound(err) => write!(f, "attribute not found: {err}"), Self::ExistingDimension(dimension) => { diff --git a/src/kem.rs b/src/kem.rs new file mode 100644 index 00000000..7ce583b4 --- /dev/null +++ b/src/kem.rs @@ -0,0 +1,805 @@ +#![allow(clippy::type_complexity)] +use crate::{ + providers::kem::mlkem::{MlKem512, MlKem768}, + traits::KemAc, + AccessPolicy, AccessStructure, Covercrypt, Error, MasterPublicKey, MasterSecretKey, + UserSecretKey, XEnc, +}; +use cosmian_crypto_core::{ + bytes_ser_de::{Deserializer, Serializable, Serializer}, + reexport::rand_core::SeedableRng, + traits::{cyclic_group_to_kem::GenericKem, KEM}, + CryptoCoreError, CsRng, +}; +use cosmian_openssl_provider::{hash::Sha256, kem::MonadicKEM, p256::P256}; +use cosmian_rust_curve25519_provider::R25519; +use zeroize::Zeroizing; + +// In order to enforce type safety, KEM objects must be tagged by the concrete +// KEM used. + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum PreQuantumKemTag { + P256, + R25519, +} + +impl Serializable for PreQuantumKemTag { + type Error = CryptoCoreError; + + fn length(&self) -> usize { + 1 + } + + fn write(&self, ser: &mut Serializer) -> Result { + match self { + Self::P256 => ser.write(&1_u64), + Self::R25519 => ser.write(&2_u64), + } + } + + fn read(de: &mut Deserializer) -> Result { + match de.read::()? { + 1 => Ok(Self::P256), + 2 => Ok(Self::R25519), + n => Err(CryptoCoreError::GenericDeserializationError(format!( + "{n} is not a valid pre-quantum-KEM tag" + ))), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum PostQuantumKemTag { + MlKem512, + MlKem768, +} + +impl Serializable for PostQuantumKemTag { + type Error = CryptoCoreError; + + fn length(&self) -> usize { + 1 + } + + fn write(&self, ser: &mut Serializer) -> Result { + match self { + Self::MlKem512 => ser.write(&1_u64), + Self::MlKem768 => ser.write(&2_u64), + } + } + + fn read(de: &mut Deserializer) -> Result { + match de.read::()? { + 1 => Ok(Self::MlKem512), + 2 => Ok(Self::MlKem768), + n => Err(CryptoCoreError::GenericDeserializationError(format!( + "{n} is not a valid post-quantum-KEM tag" + ))), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum KemTag { + PreQuantum(PreQuantumKemTag), + PostQuantum(PostQuantumKemTag), + Hybridized(PreQuantumKemTag, PostQuantumKemTag), + Abe, +} + +impl Serializable for KemTag { + type Error = CryptoCoreError; + + fn length(&self) -> usize { + match self { + Self::PreQuantum(_) | Self::PostQuantum(_) => 2, + Self::Hybridized(_, _) => 3, + Self::Abe => 1, + } + } + + fn write( + &self, + ser: &mut cosmian_crypto_core::bytes_ser_de::Serializer, + ) -> Result { + match self { + Self::PreQuantum(tag) => Ok(ser.write(&1_u64)? + ser.write(tag)?), + Self::PostQuantum(tag) => Ok(ser.write(&2_u64)? + ser.write(tag)?), + Self::Hybridized(tag1, tag2) => { + Ok(ser.write(&3_u64)? + ser.write(tag1)? + ser.write(tag2)?) + } + Self::Abe => Ok(ser.write(&4_u64)?), + } + } + + fn read(de: &mut Deserializer) -> Result { + match de.read::()? { + 1 => de.read::().map(Self::PreQuantum), + 2 => de.read::().map(Self::PostQuantum), + 3 => de + .read::<(PreQuantumKemTag, PostQuantumKemTag)>() + .map(|(tag1, tag2)| Self::Hybridized(tag1, tag2)), + 4 => Ok(Self::Abe), + n => Err(CryptoCoreError::GenericDeserializationError(format!( + "{n} is not a valid KEM tag" + ))), + } + } +} + +// In order to avoid defining one enumeration type per KEM object with one +// variant per concrete KEM option, this module uses dynamic typing on the +// concrete key and encapsulation types by to consuming and returning byte +// strings. Serialization can be used once the concrete KEM is chosen to +// retrieve the typed objects. +// +// The following functions implement this logic: they are parametric on a KEM +// type -- and thus need to be called once the concrete KEM implementation is +// known, and perform both the KEM operation and serialization/deserialization +// of the key and encapsulation objects. + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConfigurableKemDk(KemTag, Zeroizing>); + +impl ConfigurableKemDk { + pub fn new(tag: KemTag, bytes: Zeroizing>) -> Result { + // Check whether the given bytes are a valid serialization of the + // decapsulation key of the tagged KEM. + match tag { + KemTag::PreQuantum(PreQuantumKemTag::P256) => { + >::DecapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::PreQuantum(PreQuantumKemTag::R25519) => { + >::DecapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::PostQuantum(PostQuantumKemTag::MlKem512) => { + >::DecapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::PostQuantum(PostQuantumKemTag::MlKem768) => { + >::DecapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Hybridized(PreQuantumKemTag::P256, PostQuantumKemTag::MlKem512) => { + as KEM< + { P256Kem::KEY_LENGTH }, + >>::DecapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Hybridized(PreQuantumKemTag::P256, PostQuantumKemTag::MlKem768) => { + as KEM< + { P256Kem::KEY_LENGTH }, + >>::DecapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Hybridized(PreQuantumKemTag::R25519, PostQuantumKemTag::MlKem512) => { + as KEM< + { R25519Kem::KEY_LENGTH }, + >>::DecapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Hybridized(PreQuantumKemTag::R25519, PostQuantumKemTag::MlKem768) => { + as KEM< + { R25519Kem::KEY_LENGTH }, + >>::DecapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Abe => { + // For Covercrypt, the bytes can either be a valid MSK or USK. + MasterSecretKey::deserialize(&bytes) + .map_or_else( + |_| UserSecretKey::deserialize(&bytes).map(|_| ()), + |_| Ok(()), + ) + .map_err(|e| CryptoCoreError::GenericDeserializationError(e.to_string())) + } + } + .map_err(|_| { + Error::KeyError(format!( + "failed to construct a configurable-KEM decapsulation key: \ + the given bytes are not a valid decapsulation key for the KEM with tag: {tag:?}" + )) + })?; + + Ok(Self(tag, bytes)) + } + + pub fn get_tag(&self) -> KemTag { + self.0 + } + + pub fn get_bytes(&self) -> &Zeroizing> { + &self.1 + } +} + +impl Serializable for ConfigurableKemDk { + type Error = CryptoCoreError; + + fn length(&self) -> usize { + self.0.length() + self.1.length() + } + + fn write(&self, ser: &mut Serializer) -> Result { + Ok(ser.write(&self.0)? + ser.write(&self.1)?) + } + + fn read(de: &mut Deserializer) -> Result { + Ok(Self(de.read()?, de.read()?)) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConfigurableKemEk(KemTag, Zeroizing>); + +impl ConfigurableKemEk { + pub fn new(tag: KemTag, bytes: Zeroizing>) -> Result { + // Check whether the given bytes are a valid serialization of the + // encapsulation key of the tagged KEM. + match tag { + KemTag::PreQuantum(PreQuantumKemTag::P256) => { + >::EncapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::PreQuantum(PreQuantumKemTag::R25519) => { + >::EncapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::PostQuantum(PostQuantumKemTag::MlKem512) => { + >::EncapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::PostQuantum(PostQuantumKemTag::MlKem768) => { + >::EncapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Hybridized(PreQuantumKemTag::P256, PostQuantumKemTag::MlKem512) => { + as KEM< + { P256Kem::KEY_LENGTH }, + >>::EncapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Hybridized(PreQuantumKemTag::P256, PostQuantumKemTag::MlKem768) => { + as KEM< + { P256Kem::KEY_LENGTH }, + >>::EncapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Hybridized(PreQuantumKemTag::R25519, PostQuantumKemTag::MlKem512) => { + as KEM< + { R25519Kem::KEY_LENGTH }, + >>::EncapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Hybridized(PreQuantumKemTag::R25519, PostQuantumKemTag::MlKem768) => { + as KEM< + { R25519Kem::KEY_LENGTH }, + >>::EncapsulationKey::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Abe => MasterPublicKey::deserialize(&bytes) + .map(|_| ()) + .map_err(|e| CryptoCoreError::GenericDeserializationError(e.to_string())), + } + .map_err(|_| { + Error::KeyError(format!( + "failed to construct a configurable-KEM encapsulation key: \ + the given bytes are not a valid encapsulation key for the KEM with tag: {tag:?}" + )) + })?; + + Ok(Self(tag, bytes)) + } + + pub fn get_tag(&self) -> KemTag { + self.0 + } + + pub fn get_bytes(&self) -> &Zeroizing> { + &self.1 + } +} + +impl Serializable for ConfigurableKemEk { + type Error = CryptoCoreError; + + fn length(&self) -> usize { + self.0.length() + self.1.length() + } + + fn write(&self, ser: &mut Serializer) -> Result { + Ok(ser.write(&self.0)? + ser.write(&self.1)?) + } + + fn read(de: &mut Deserializer) -> Result { + Ok(Self(de.read()?, de.read()?)) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConfigurableKemEnc(KemTag, Zeroizing>); + +impl ConfigurableKemEnc { + pub fn new(tag: KemTag, bytes: Zeroizing>) -> Result { + // Check whether the given bytes are a valid serialization of the + // encapsulation of the tagged KEM. + match tag { + KemTag::PreQuantum(PreQuantumKemTag::P256) => { + >::Encapsulation::deserialize(&bytes) + .map(|_| ()) + } + KemTag::PreQuantum(PreQuantumKemTag::R25519) => { + >::Encapsulation::deserialize(&bytes) + .map(|_| ()) + } + KemTag::PostQuantum(PostQuantumKemTag::MlKem512) => { + >::Encapsulation::deserialize(&bytes) + .map(|_| ()) + } + KemTag::PostQuantum(PostQuantumKemTag::MlKem768) => { + >::Encapsulation::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Hybridized(PreQuantumKemTag::P256, PostQuantumKemTag::MlKem512) => { + as KEM< + { P256Kem::KEY_LENGTH }, + >>::Encapsulation::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Hybridized(PreQuantumKemTag::P256, PostQuantumKemTag::MlKem768) => { + as KEM< + { P256Kem::KEY_LENGTH }, + >>::Encapsulation::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Hybridized(PreQuantumKemTag::R25519, PostQuantumKemTag::MlKem512) => { + as KEM< + { R25519Kem::KEY_LENGTH }, + >>::Encapsulation::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Hybridized(PreQuantumKemTag::R25519, PostQuantumKemTag::MlKem768) => { + as KEM< + { R25519Kem::KEY_LENGTH }, + >>::Encapsulation::deserialize(&bytes) + .map(|_| ()) + } + KemTag::Abe => { + // For Covercrypt, the bytes can either be a valid MSK or USK. + XEnc::deserialize(&bytes) + .map(|_| ()) + .map_err(|e| CryptoCoreError::GenericDeserializationError(e.to_string())) + } + } + .map_err(|_| { + Error::KeyError(format!( + "failed to construct a configurable-KEM encapsulation: \ + the given bytes are not a valid encapsulation for the KEM with tag: {tag:?}" + )) + })?; + + Ok(Self(tag, bytes)) + } + + pub fn get_tag(&self) -> KemTag { + self.0 + } + + pub fn get_bytes(&self) -> &Zeroizing> { + &self.1 + } +} + +impl Serializable for ConfigurableKemEnc { + type Error = CryptoCoreError; + + fn length(&self) -> usize { + self.0.length() + self.1.length() + } + + fn write(&self, ser: &mut Serializer) -> Result { + Ok(ser.write(&self.0)? + ser.write(&self.1)?) + } + + fn read(de: &mut Deserializer) -> Result { + Ok(Self(de.read()?, de.read()?)) + } +} + +#[allow(clippy::type_complexity)] +fn generic_keygen>( + tag: KemTag, +) -> Result<(ConfigurableKemDk, ConfigurableKemEk), Error> +where + Kem::DecapsulationKey: Serializable, +{ + let mut rng = CsRng::from_entropy(); + let (dk, ek) = Kem::keygen(&mut rng).map_err(|e| Error::Kem(e.to_string()))?; + Ok(( + ConfigurableKemDk( + tag, + dk.serialize().map_err(|e| { + Error::ConversionFailed(format!( + "failed serializing the decapsulation key in configurable KEM: {e}" + )) + })?, + ), + ConfigurableKemEk( + tag, + ek.serialize().map_err(|e| { + Error::ConversionFailed(format!( + "failed serializing the encapsulation key in configurable KEM: {e}" + )) + })?, + ), + )) +} + +fn generic_enc>( + ek: &ConfigurableKemEk, +) -> Result<(Zeroizing>, ConfigurableKemEnc), Error> { + let tag = ek.get_tag(); + let ek = + >::EncapsulationKey::deserialize(ek.get_bytes()).map_err(|e| { + Error::ConversionFailed(format!( + "failed deserializing the encapsulation key in configurable KEM: {e}" + )) + })?; + + let mut rng = CsRng::from_entropy(); + let (key, enc) = Kem::enc(&ek, &mut rng) + .map_err(|e| Error::Kem(format!("configurable-KEM encapsulation error: {e}")))?; + + Ok(( + key.serialize()?, + ConfigurableKemEnc( + tag, + enc.serialize().map_err(|e| { + Error::ConversionFailed(format!( + "failed serializing the encapsulation in configurable KEM: {e}" + )) + })?, + ), + )) +} + +fn generic_dec>( + dk: &ConfigurableKemDk, + enc: &ConfigurableKemEnc, +) -> Result>, Error> +where + Kem::DecapsulationKey: Serializable, +{ + let tag = dk.get_tag(); + + if tag != enc.get_tag() { + return Err(Error::OperationNotPermitted(format!( + "heterogeneous decapsulation-key and encapsulation tags: {tag:?} != {:?}", + enc.get_tag() + ))); + } + + let dk = + >::DecapsulationKey::deserialize(dk.get_bytes()).map_err(|e| { + Error::ConversionFailed(format!( + "failed deserializing the decapsulation key in configurable KEM: {e}" + )) + })?; + + let enc = + >::Encapsulation::deserialize(enc.get_bytes()).map_err(|e| { + Error::ConversionFailed(format!( + "failed deserializing the encapsulation in configurable KEM: {e}" + )) + })?; + + let key = Kem::dec(&dk, &enc) + .map_err(|e| Error::Kem(format!("configurable-KEM decapsulation error: {e}")))?; + + Ok(key.serialize()?) +} + +// We can now implement a KEM-like interface for our configurable KEM which +// deserializes KEM objects as couple (tag, bytes), checks tag legality and +// compatibility across objects before the KEM operation with corresponding +// implementation, and finally serializes returned objects as (tag, bytes) +// couples. + +type P256Kem = MonadicKEM<32, P256, Sha256>; +type R25519Kem = GenericKem<32, R25519, Sha256>; + +// Even though lengths of the keys encapsulated by the two combined KEM schemes +// can vary, it is much simpler to enforce their equality, which is performed +// here by binding the three key lengths required by the KEM combiner to the +// same one. +type KemCombiner = + cosmian_crypto_core::traits::kem_combiner::KemCombiner< + LENGTH, + LENGTH, + LENGTH, + Kem1, + Kem2, + Sha256, // SHA256 from the OpenSSL provider. + >; + +pub struct ConfigurableKEM; + +impl ConfigurableKEM { + #[allow(clippy::too_many_arguments)] + pub fn keygen( + tag: KemTag, + access_structure: Option, + ) -> Result<(ConfigurableKemDk, ConfigurableKemEk), Error> { + match tag { + KemTag::PreQuantum(PreQuantumKemTag::P256) => { + generic_keygen::<{ P256Kem::KEY_LENGTH }, P256Kem>(tag) + } + KemTag::PreQuantum(PreQuantumKemTag::R25519) => { + generic_keygen::<{ R25519Kem::KEY_LENGTH }, R25519Kem>(tag) + } + KemTag::PostQuantum(PostQuantumKemTag::MlKem512) => { + generic_keygen::<{ MlKem512::KEY_LENGTH }, MlKem512>(tag) + } + KemTag::PostQuantum(PostQuantumKemTag::MlKem768) => { + generic_keygen::<{ MlKem768::KEY_LENGTH }, MlKem768>(tag) + } + KemTag::Hybridized(PreQuantumKemTag::P256, PostQuantumKemTag::MlKem512) => { + generic_keygen::< + { P256Kem::KEY_LENGTH }, + KemCombiner<{ P256Kem::KEY_LENGTH }, P256Kem, MlKem512>, + >(tag) + } + KemTag::Hybridized(PreQuantumKemTag::P256, PostQuantumKemTag::MlKem768) => { + generic_keygen::< + { P256Kem::KEY_LENGTH }, + KemCombiner<{ P256Kem::KEY_LENGTH }, P256Kem, MlKem768>, + >(tag) + } + KemTag::Hybridized(PreQuantumKemTag::R25519, PostQuantumKemTag::MlKem512) => { + generic_keygen::< + { R25519Kem::KEY_LENGTH }, + KemCombiner<{ R25519Kem::KEY_LENGTH }, R25519Kem, MlKem512>, + >(tag) + } + KemTag::Hybridized(PreQuantumKemTag::R25519, PostQuantumKemTag::MlKem768) => { + generic_keygen::< + { R25519Kem::KEY_LENGTH }, + KemCombiner<{ R25519Kem::KEY_LENGTH }, R25519Kem, MlKem768>, + >(tag) + } + KemTag::Abe => { + let access_structure = access_structure.ok_or_else(|| { + Error::OperationNotPermitted( + "cannot execute a Covercrypt key generation without an access structure" + .to_owned(), + ) + })?; + let cc = Covercrypt::default(); + let (mut msk, _) = cc.setup()?; + msk.access_structure = access_structure; + let mpk = cc.update_msk(&mut msk)?; + Ok(( + ConfigurableKemDk(tag, msk.serialize()?), + ConfigurableKemEk(tag, mpk.serialize()?), + )) + } + } + } + + pub fn enc( + ek: &ConfigurableKemEk, + access_policy: Option<&AccessPolicy>, + ) -> Result<(Zeroizing>, ConfigurableKemEnc), Error> { + match ek.get_tag() { + KemTag::PreQuantum(PreQuantumKemTag::P256) => { + generic_enc::<{ P256Kem::KEY_LENGTH }, P256Kem>(ek) + } + KemTag::PreQuantum(PreQuantumKemTag::R25519) => { + generic_enc::<{ R25519Kem::KEY_LENGTH }, R25519Kem>(ek) + } + KemTag::PostQuantum(PostQuantumKemTag::MlKem512) => { + generic_enc::<{ MlKem512::KEY_LENGTH }, MlKem512>(ek) + } + KemTag::PostQuantum(PostQuantumKemTag::MlKem768) => { + generic_enc::<{ MlKem768::KEY_LENGTH }, MlKem768>(ek) + } + KemTag::Hybridized(PreQuantumKemTag::P256, PostQuantumKemTag::MlKem512) => { + generic_enc::< + { P256Kem::KEY_LENGTH }, + KemCombiner<{ P256Kem::KEY_LENGTH }, P256Kem, MlKem512>, + >(ek) + } + KemTag::Hybridized(PreQuantumKemTag::P256, PostQuantumKemTag::MlKem768) => { + generic_enc::< + { P256Kem::KEY_LENGTH }, + KemCombiner<{ P256Kem::KEY_LENGTH }, P256Kem, MlKem768>, + >(ek) + } + KemTag::Hybridized(PreQuantumKemTag::R25519, PostQuantumKemTag::MlKem512) => { + generic_enc::< + { R25519Kem::KEY_LENGTH }, + KemCombiner<{ R25519Kem::KEY_LENGTH }, R25519Kem, MlKem512>, + >(ek) + } + KemTag::Hybridized(PreQuantumKemTag::R25519, PostQuantumKemTag::MlKem768) => { + generic_enc::< + { R25519Kem::KEY_LENGTH }, + KemCombiner<{ R25519Kem::KEY_LENGTH }, R25519Kem, MlKem768>, + >(ek) + } + KemTag::Abe => { + let ap = access_policy.ok_or_else(|| { + Error::OperationNotPermitted( + "cannot create a Covercrypt encapsulation without an access policy" + .to_owned(), + ) + })?; + let tag = ek.get_tag(); + let mpk = MasterPublicKey::deserialize(ek.get_bytes())?; + let (key, enc) = Covercrypt::default().encaps(&mpk, ap)?; + Ok((key.serialize()?, ConfigurableKemEnc(tag, enc.serialize()?))) + } + } + } + + pub fn dec( + dk: &ConfigurableKemDk, + enc: &ConfigurableKemEnc, + ) -> Result>, Error> { + match dk.get_tag() { + KemTag::PreQuantum(PreQuantumKemTag::P256) => { + generic_dec::<{ P256Kem::KEY_LENGTH }, P256Kem>(dk, enc) + } + KemTag::PreQuantum(PreQuantumKemTag::R25519) => { + generic_dec::<{ R25519Kem::KEY_LENGTH }, R25519Kem>(dk, enc) + } + KemTag::PostQuantum(PostQuantumKemTag::MlKem512) => { + generic_dec::<{ MlKem512::KEY_LENGTH }, MlKem512>(dk, enc) + } + KemTag::PostQuantum(PostQuantumKemTag::MlKem768) => { + generic_dec::<{ MlKem768::KEY_LENGTH }, MlKem768>(dk, enc) + } + KemTag::Hybridized(PreQuantumKemTag::P256, PostQuantumKemTag::MlKem512) => { + generic_dec::< + { P256Kem::KEY_LENGTH }, + KemCombiner<{ P256Kem::KEY_LENGTH }, P256Kem, MlKem512>, + >(dk, enc) + } + KemTag::Hybridized(PreQuantumKemTag::P256, PostQuantumKemTag::MlKem768) => { + generic_dec::< + { P256Kem::KEY_LENGTH }, + KemCombiner<{ P256Kem::KEY_LENGTH }, P256Kem, MlKem768>, + >(dk, enc) + } + KemTag::Hybridized(PreQuantumKemTag::R25519, PostQuantumKemTag::MlKem512) => { + generic_dec::< + { R25519Kem::KEY_LENGTH }, + KemCombiner<{ R25519Kem::KEY_LENGTH }, R25519Kem, MlKem512>, + >(dk, enc) + } + KemTag::Hybridized(PreQuantumKemTag::R25519, PostQuantumKemTag::MlKem768) => { + generic_dec::< + { R25519Kem::KEY_LENGTH }, + KemCombiner<{ R25519Kem::KEY_LENGTH }, R25519Kem, MlKem768>, + >(dk, enc) + } + KemTag::Abe => { + if enc.get_tag() != KemTag::Abe { + return Err(Error::OperationNotPermitted(format!( + "heterogeneous decapsulation-key and encapsulation tags: {:?} != {:?}", + KemTag::Abe, + enc.get_tag(), + ))); + } + + let enc = XEnc::deserialize(enc.get_bytes()).map_err(|e| { + Error::ConversionFailed(format!( + "failed deserializing the CoverCrypt encapsulation in configurable KEM: {e}" + )) + })?; + + let usk = UserSecretKey::deserialize(dk.get_bytes()).map_err(|e| { + Error::ConversionFailed(format!( + "failed deserializing the CoverCrypt encapsulation in configurable KEM: {e}" + )) + })?; + + let key = Covercrypt::default().decaps(&usk, &enc)?.ok_or_else(|| { + Error::OperationNotPermitted( + "cannot open Covercrypt encapsulation: incompatible access rights" + .to_owned(), + ) + })?; + + Ok(key.serialize()?) + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::gen_structure; + + use super::*; + use cosmian_crypto_core::bytes_ser_de::test_serialization; + + #[test] + fn test_tag_serialization() { + // Exhaustively test serializations. + test_serialization(&KemTag::PreQuantum(PreQuantumKemTag::P256)).unwrap(); + test_serialization(&KemTag::PreQuantum(PreQuantumKemTag::R25519)).unwrap(); + test_serialization(&KemTag::PostQuantum(PostQuantumKemTag::MlKem512)).unwrap(); + test_serialization(&KemTag::Hybridized( + PreQuantumKemTag::P256, + PostQuantumKemTag::MlKem512, + )) + .unwrap(); + test_serialization(&KemTag::Hybridized( + PreQuantumKemTag::R25519, + PostQuantumKemTag::MlKem512, + )) + .unwrap(); + test_serialization(&KemTag::Abe).unwrap(); + } + + #[test] + fn test_configurable_kem() { + fn run_test(tag: KemTag) { + let (dk, ek) = ConfigurableKEM::keygen(tag, None).unwrap(); + test_serialization(&dk).unwrap(); + test_serialization(&ek).unwrap(); + let (key, enc) = ConfigurableKEM::enc(&ek, None).unwrap(); + test_serialization(&enc).unwrap(); + let key_ = ConfigurableKEM::dec(&dk, &enc).unwrap(); + assert_eq!(key, key_); + } + + run_test(KemTag::PreQuantum(PreQuantumKemTag::P256)); + + run_test(KemTag::PreQuantum(PreQuantumKemTag::R25519)); + + run_test(KemTag::PostQuantum(PostQuantumKemTag::MlKem512)); + + run_test(KemTag::PostQuantum(PostQuantumKemTag::MlKem768)); + + run_test(KemTag::Hybridized( + PreQuantumKemTag::P256, + PostQuantumKemTag::MlKem512, + )); + + run_test(KemTag::Hybridized( + PreQuantumKemTag::P256, + PostQuantumKemTag::MlKem768, + )); + + run_test(KemTag::Hybridized( + PreQuantumKemTag::R25519, + PostQuantumKemTag::MlKem512, + )); + run_test(KemTag::Hybridized( + PreQuantumKemTag::R25519, + PostQuantumKemTag::MlKem768, + )); + + println!("testing CoverCrypt ABE..."); + let mut access_structure = AccessStructure::new(); + gen_structure(&mut access_structure, true).unwrap(); + let usk_access_policy = AccessPolicy::parse("DPT::MKG").unwrap(); + let enc_access_policy = AccessPolicy::parse("*").unwrap(); + + let (msk, mpk) = ConfigurableKEM::keygen(KemTag::Abe, Some(access_structure)).unwrap(); + let tag = msk.get_tag(); + let mut msk = MasterSecretKey::deserialize(msk.get_bytes()).unwrap(); + let usk = Covercrypt::default() + .generate_user_secret_key(&mut msk, &usk_access_policy) + .unwrap(); + let usk = ConfigurableKemDk::new(tag, usk.serialize().unwrap()).unwrap(); + let (key, enc) = ConfigurableKEM::enc(&mpk, Some(&enc_access_policy)).unwrap(); + let key_ = ConfigurableKEM::dec(&usk, &enc).unwrap(); + assert_eq!(key, key_); + } +} diff --git a/src/lib.rs b/src/lib.rs index 406a1448..f8e3cb81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,29 +8,15 @@ //! the DDH and LWE", T. Brézot, P. de Perthuis and D. Pointcheval 2023. //! [2] "A Proposal for an ISO Standard for Public Key Encryption (version 2.1)", Shoup 2001. -mod error; - -mod abe_policy; -mod ae; -mod core; +mod abe; mod data_struct; -mod encrypted_header; - -pub mod api; -pub mod traits; - -pub use abe_policy::{AccessStructure, QualifiedAttribute, SecurityMode}; +mod error; +mod kem; +mod providers; #[cfg(any(test, feature = "test-utils"))] -pub mod test_utils; - -#[cfg(feature = "test-utils")] -pub use abe_policy::gen_structure; - -#[cfg(feature = "test-utils")] -pub use test_utils::cc_keygen; +mod test_utils; -pub use self::core::{MasterPublicKey, MasterSecretKey, UserSecretKey, XEnc}; -pub use abe_policy::AccessPolicy; -pub use encrypted_header::{CleartextHeader, EncryptedHeader}; +pub use abe::*; pub use error::Error; +pub use kem::*; diff --git a/src/providers.rs b/src/providers.rs new file mode 100644 index 00000000..a50688f8 --- /dev/null +++ b/src/providers.rs @@ -0,0 +1,5 @@ +pub mod kem; +mod nike; + +pub(crate) use kem::MlKem; +pub(crate) use nike::ElGamal; diff --git a/src/core/kem.rs b/src/providers/kem.rs similarity index 71% rename from src/core/kem.rs rename to src/providers/kem.rs index f798da4a..47e9576e 100644 --- a/src/core/kem.rs +++ b/src/providers/kem.rs @@ -4,7 +4,7 @@ compile_error!("only one MLKEM version can be chosen at a time"); pub mod mlkem; #[cfg(feature = "mlkem-512")] -pub use mlkem::MlKem512 as MlKem; +pub(crate) use mlkem::MlKem512 as MlKem; #[cfg(feature = "mlkem-768")] -pub use mlkem::MlKem768 as MlKem; +pub(crate) use mlkem::MlKem768 as MlKem; diff --git a/src/core/kem/mlkem.rs b/src/providers/kem/mlkem.rs similarity index 81% rename from src/core/kem/mlkem.rs rename to src/providers/kem/mlkem.rs index d3817919..da7932c4 100644 --- a/src/core/kem/mlkem.rs +++ b/src/providers/kem/mlkem.rs @@ -1,8 +1,13 @@ -use crate::{core::SHARED_SECRET_LENGTH, traits::Kem, Error}; +use crate::Error; +use core::ops::Deref; use cosmian_crypto_core::{ bytes_ser_de::{Deserializer, Serializable, Serializer}, - reexport::{rand_core::CryptoRngCore, zeroize::Zeroize}, - Secret, + reexport::{ + rand_core::CryptoRngCore, + zeroize::{Zeroize, ZeroizeOnDrop}, + }, + traits::KEM, + CryptoCoreError, Secret, SymmetricKey, }; use ml_kem::{ array::Array, @@ -10,13 +15,21 @@ use ml_kem::{ EncodedSizeUser, KemCore, }; +pub const KEY_LENGTH: usize = 32; + macro_rules! make_mlkem { ($base: ident, $ek: ident, $ek_len: literal, $dk: ident, $dk_len: literal, $enc: ident, $enc_len:literal) => { #[derive(Debug, PartialEq, Clone)] pub struct $ek(Box<::EncapsulationKey>); + impl From<&$dk> for $ek { + fn from(dk: &$dk) -> Self { + Self(Box::new(dk.0.encapsulation_key().clone())) + } + } + impl Serializable for $ek { - type Error = Error; + type Error = CryptoCoreError; fn length(&self) -> usize { $ek_len @@ -40,6 +53,9 @@ macro_rules! make_mlkem { #[derive(Debug, Clone, PartialEq)] pub struct $dk(Box<::DecapsulationKey>); + // DecapsulationKey implements ZeroizeOnDrop. + impl ZeroizeOnDrop for $dk {} + #[allow(dead_code)] impl $dk { pub fn ek(&self) -> $ek { @@ -48,7 +64,7 @@ macro_rules! make_mlkem { } impl Serializable for $dk { - type Error = Error; + type Error = CryptoCoreError; fn length(&self) -> usize { $dk_len @@ -72,15 +88,23 @@ macro_rules! make_mlkem { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct $enc(Box::CiphertextSize>>); + impl Deref for $enc { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.0.as_slice() + } + } + impl Serializable for $enc { - type Error = Error; + type Error = CryptoCoreError; fn length(&self) -> usize { $enc_len } fn write(&self, ser: &mut Serializer) -> Result { - Ok(ser.write_array(&self.0)?) + ser.write_array(&self.0) } fn read(de: &mut Deserializer) -> Result { @@ -91,15 +115,13 @@ macro_rules! make_mlkem { } } + #[derive(Debug, Copy, Clone)] pub struct $base; - impl Kem for $base { + impl KEM for $base { type EncapsulationKey = $ek; type DecapsulationKey = $dk; - type SessionKey = Secret; - type Encapsulation = $enc; - type Error = Error; fn keygen( @@ -112,29 +134,28 @@ macro_rules! make_mlkem { fn enc( ek: &Self::EncapsulationKey, rng: &mut impl CryptoRngCore, - ) -> Result<(Self::SessionKey, Self::Encapsulation), Self::Error> { + ) -> Result<(SymmetricKey, Self::Encapsulation), Self::Error> { let (enc, mut ss) = ek.0.encapsulate(rng) .map_err(|e| Error::Kem(format!("{:?}", e)))?; let ss = Secret::from_unprotected_bytes(ss.as_mut()); - Ok((ss, $enc(Box::new(enc)))) + Ok((ss.into(), $enc(Box::new(enc)))) } fn dec( dk: &Self::DecapsulationKey, enc: &Self::Encapsulation, - ) -> Result { + ) -> Result, Self::Error> { let mut ss = dk.0.decapsulate(&enc.0) .map_err(|e| Self::Error::Kem(format!("{e:?}")))?; let ss = Secret::from_unprotected_bytes(ss.as_mut()); - Ok(ss) + Ok(ss.into()) } } }; } -#[cfg(feature = "mlkem-512")] make_mlkem!( MlKem512, EncapsulationKey512, @@ -145,7 +166,6 @@ make_mlkem!( 768 ); -#[cfg(feature = "mlkem-768")] make_mlkem!( MlKem768, EncapsulationKey768, @@ -180,8 +200,6 @@ mod tests { }; } - #[cfg(feature = "mlkem-512")] test_mlkem!(MlKem512, test_mlkem512); - #[cfg(feature = "mlkem-768")] test_mlkem!(MlKem768, test_mlkem768); } diff --git a/src/core/nike.rs b/src/providers/nike.rs similarity index 55% rename from src/core/nike.rs rename to src/providers/nike.rs index 6ad18b96..6f496341 100644 --- a/src/core/nike.rs +++ b/src/providers/nike.rs @@ -2,13 +2,7 @@ compile_error!("only one elliptic curve can be chosen at a time"); #[cfg(feature = "curve25519")] -mod r25519; - -#[cfg(feature = "curve25519")] -pub use r25519::R25519 as ElGamal; - -#[cfg(feature = "p-256")] -mod p256; +pub use cosmian_rust_curve25519_provider::R25519 as ElGamal; #[cfg(feature = "p-256")] -pub use p256::P256 as ElGamal; +pub use cosmian_openssl_provider::p256::P256 as ElGamal; diff --git a/src/test_utils/mod.rs b/src/test_utils/mod.rs index 8ad782d2..bb16f4ae 100644 --- a/src/test_utils/mod.rs +++ b/src/test_utils/mod.rs @@ -1,6 +1,8 @@ -use crate::{abe_policy::gen_structure, api::Covercrypt, Error, MasterPublicKey, MasterSecretKey}; - -//pub mod non_regression; +use crate::{ + abe::gen_structure, + abe::{Covercrypt, MasterPublicKey, MasterSecretKey}, + Error, +}; /// Creates the test access structure. pub fn cc_keygen( @@ -17,11 +19,9 @@ pub fn cc_keygen( mod tests { use super::*; - use crate::{ - abe_policy::{AccessPolicy, QualifiedAttribute}, - api::Covercrypt, - traits::KemAc, - EncryptedHeader, SecurityMode, + use crate::abe::{ + encrypted_header::EncryptedHeader, traits::KemAc, AccessPolicy, Covercrypt, EncryptionHint, + QualifiedAttribute, }; #[test] @@ -34,7 +34,7 @@ mod tests { let _ = &mut msk.access_structure.add_attribute( QualifiedAttribute::new("DPT", "Sales"), - SecurityMode::PreQuantum, + EncryptionHint::Classic, None, )?; let mpk = cc.update_msk(&mut msk)?; diff --git a/src/traits.rs b/src/traits.rs index 6cb9604d..8b137891 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,198 +1 @@ -use cosmian_crypto_core::{ - reexport::{rand_core::CryptoRngCore, zeroize::Zeroizing}, - Secret, SymmetricKey, -}; -use std::ops::Add; -use std::ops::AddAssign; -use std::ops::Div; -use std::ops::Mul; -use std::ops::MulAssign; -use std::ops::Sub; -use std::ops::SubAssign; -use crate::AccessPolicy; - -pub trait KemAc { - type EncapsulationKey; - type DecapsulationKey; - type Encapsulation; - type Error: std::error::Error; - - /// Generates a new encapsulation for the given access policy. - /// - /// # Error - /// - /// Returns an error if the access policy is not valid. - fn encaps( - &self, - ek: &Self::EncapsulationKey, - ap: &AccessPolicy, - ) -> Result<(Secret, Self::Encapsulation), Self::Error>; - - /// Attempts opening the given encapsulation with the given key. Returns the encapsulated - /// secret upon success or `None` if this key was not authorized to open this encapsulation. - fn decaps( - &self, - dk: &Self::DecapsulationKey, - enc: &Self::Encapsulation, - ) -> Result>, Self::Error>; -} - -pub trait AE { - type Error: std::error::Error; - - /// Encrypts the given plaintext using the given key. - fn encrypt( - rng: &mut impl CryptoRngCore, - key: &SymmetricKey, - ptx: &[u8], - ) -> Result, Self::Error>; - - /// Decrypts the given ciphertext using the given key. - /// - /// # Error - /// - /// Returns an error if the integrity of the ciphertext could not be verified. - fn decrypt( - key: &SymmetricKey, - ctx: &[u8], - ) -> Result>, Self::Error>; -} - -pub trait PkeAc> { - type EncryptionKey; - type DecryptionKey; - type Ciphertext; - type Error: std::error::Error; - - /// Encrypts the given plaintext under the given access policy. - /// - /// # Error - /// - /// Returns an error if the access policy is not valid. - fn encrypt( - &self, - ek: &Self::EncryptionKey, - ap: &AccessPolicy, - ptx: &[u8], - ) -> Result; - - /// Attempts decrypting the given ciphertext with the given key. Returns the - /// plaintext upon success, or `None` if this key was not authorized to - /// decrypt this ciphertext. - fn decrypt( - &self, - dk: &Self::DecryptionKey, - ctx: &Self::Ciphertext, - ) -> Result>>, Self::Error>; -} - -pub trait Kem { - type EncapsulationKey; - type DecapsulationKey; - type SessionKey; - type Encapsulation; - type Error: std::error::Error; - - /// Generates a new random keypair. - fn keygen( - rng: &mut impl CryptoRngCore, - ) -> Result<(Self::DecapsulationKey, Self::EncapsulationKey), Self::Error>; - - /// Generates an encapsulation of a random session key, and returns both the - /// key and its encapsulation. - fn enc( - ek: &Self::EncapsulationKey, - rng: &mut impl CryptoRngCore, - ) -> Result<(Self::SessionKey, Self::Encapsulation), Self::Error>; - - /// Attempts opening the given encapsulation. Upon failure to decapsulate, - /// returns a random session key. - fn dec( - dk: &Self::DecapsulationKey, - enc: &Self::Encapsulation, - ) -> Result; -} - -pub trait Nike { - type SecretKey: Sampling; - type PublicKey: for<'a> From<&'a Self::SecretKey>; - type SessionKey; - type Error: std::error::Error; - - /// Generates a new random keypair. - fn keygen( - rng: &mut impl CryptoRngCore, - ) -> Result<(Self::SecretKey, Self::PublicKey), Self::Error>; - - /// Generates the session key associated to the given keypair. - fn session_key( - sk: &Self::SecretKey, - pk: &Self::PublicKey, - ) -> Result; -} - -pub trait Sampling { - fn random(rng: &mut impl CryptoRngCore) -> Self; - fn hash(seed: &[u8]) -> Self; -} - -pub trait Zero { - fn zero() -> Self; - fn is_zero(&self) -> bool; -} - -pub trait One { - fn one() -> Self; - fn is_one(&self) -> bool; -} - -pub trait Group: - Sized - + Zero - + Add - + AddAssign - + Sub - + SubAssign - + for<'a> Add<&'a Self, Output = Self> - + for<'a> Sub<&'a Self, Output = Self> -where - for<'a, 'b> &'a Self: Add<&'b Self, Output = Self>, - for<'a, 'b> &'a Self: Sub<&'b Self, Output = Self>, -{ -} - -pub trait Ring: - Group - + Zero - + Mul - + MulAssign - + Div> - + for<'a> Mul<&'a Self, Output = Self> - + for<'a> Div<&'a Self, Output = Result> -where - for<'a, 'b> &'a Self: Add<&'b Self, Output = Self>, - for<'a, 'b> &'a Self: Sub<&'b Self, Output = Self>, - for<'a, 'b> &'a Self: Mul<&'b Self, Output = Self>, - for<'a, 'b> &'a Self: Div<&'b Self, Output = Result>, -{ - type DivError; -} - -pub trait KeyHomomorphicNike: Nike -where - Self::PublicKey: Group, - Self::SecretKey: Ring, - Self::PublicKey: Mul, - for<'a> Self::PublicKey: Mul<&'a Self::SecretKey, Output = Self::PublicKey>, - for<'a, 'b> &'a Self::PublicKey: Add<&'b Self::PublicKey, Output = Self::PublicKey>, - for<'a, 'b> &'a Self::PublicKey: Sub<&'b Self::PublicKey, Output = Self::PublicKey>, - for<'a, 'b> &'a Self::SecretKey: Add<&'b Self::SecretKey, Output = Self::SecretKey>, - for<'a, 'b> &'a Self::SecretKey: Sub<&'b Self::SecretKey, Output = Self::SecretKey>, - for<'a, 'b> &'a Self::SecretKey: Mul<&'b Self::SecretKey, Output = Self::SecretKey>, - for<'a, 'b> &'a Self::SecretKey: Div< - &'b Self::SecretKey, - Output = Result::DivError>, - >, -{ -}