From 5625c5db5fee0dcb0de2da60a34df8f5ed075aec Mon Sep 17 00:00:00 2001 From: Alex Emmet <111742636+Alex-Emmet@users.noreply.github.com> Date: Fri, 3 Jul 2026 06:28:06 +0200 Subject: [PATCH] Updated Crypto to use MTP-Crypto --- Cargo.lock | 449 +++++++++++++++++--- client/src/client_connection.rs | 33 +- iota-cli/src/elements/console_card.rs | 2 +- iota-storage/src/users/user_manager.rs | 17 +- iota-storage/src/util/config_util.rs | 4 + iota-util/Cargo.toml | 6 +- iota-util/src/crypto_helper.rs | 161 ++----- iota-util/src/crypto_util.rs | 204 ++------- omikron-connector/src/omikron_connection.rs | 109 ++--- omikron-connector/src/user_ops.rs | 26 +- 10 files changed, 558 insertions(+), 453 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1fff38f..b53ec44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,7 +73,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rand 0.10.1", + "rand 0.10.2", "sha1 0.11.0", "smallvec", "tokio", @@ -424,9 +424,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" dependencies = [ "aws-lc-sys", "untrusted 0.7.1", @@ -435,14 +435,15 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.41.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" dependencies = [ "cc", "cmake", "dunce", "fs_extra", + "pkg-config", ] [[package]] @@ -451,6 +452,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "bit-set" version = "0.5.3" @@ -607,6 +614,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + [[package]] name = "chacha20" version = "0.10.1" @@ -618,6 +636,19 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20 0.9.1", + "cipher", + "poly1305", + "zeroize", +] + [[package]] name = "chrono" version = "0.4.45" @@ -639,6 +670,7 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common 0.1.7", "inout", + "zeroize", ] [[package]] @@ -656,7 +688,7 @@ dependencies = [ "futures", "futures-util", "hex", - "hkdf", + "hkdf 0.12.4", "hyper", "hyper-util", "iota-auth", @@ -701,6 +733,12 @@ dependencies = [ "cc", ] +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "combine" version = "4.6.7" @@ -725,6 +763,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "const-oid" version = "0.10.2" @@ -890,7 +934,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ + "getrandom 0.4.3", "hybrid-array", + "rand_core 0.10.1", ] [[package]] @@ -912,6 +958,42 @@ dependencies = [ "cipher", ] +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto 0.2.9", + "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 2.0.118", +] + [[package]] name = "darling" version = "0.23.0" @@ -978,6 +1060,27 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid 0.9.6", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" +dependencies = [ + "const-oid 0.10.2", + "zeroize", +] + [[package]] name = "der-parser" version = "10.0.0" @@ -1050,8 +1153,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer 0.12.1", - "const-oid", + "const-oid 0.10.2", "crypto-common 0.2.2", + "ctutils", ] [[package]] @@ -1080,13 +1184,37 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8 0.10.2", + "signature 2.2.0", +] + +[[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 0.10.9", + "subtle", + "zeroize", +] + [[package]] name = "ed448-goldilocks" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87b5fa9e9e3dd5fe1369f380acd3dcdfa766dbd0a1cd5b048fb40e38a6a78e79" dependencies = [ - "fiat-crypto", + "fiat-crypto 0.1.20", "hex", "subtle", ] @@ -1171,6 +1299,12 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "filedescriptor" version = "0.8.3" @@ -1523,7 +1657,16 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "hmac", + "hmac 0.12.1", +] + +[[package]] +name = "hkdf" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" +dependencies = [ + "hmac 0.13.0", ] [[package]] @@ -1535,6 +1678,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", +] + [[package]] name = "httlib-huffman" version = "0.3.4" @@ -1603,6 +1755,7 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" dependencies = [ + "ctutils", "typenum", ] @@ -1660,7 +1813,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.4", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -1867,7 +2020,7 @@ dependencies = [ "futures", "futures-util", "hex", - "hkdf", + "hkdf 0.12.4", "hyper", "hyper-util", "iota-logger", @@ -1917,7 +2070,7 @@ dependencies = [ "futures", "futures-util", "hex", - "hkdf", + "hkdf 0.12.4", "hyper", "hyper-util", "iota-logger", @@ -2009,7 +2162,7 @@ dependencies = [ "aes-gcm", "base64", "hex", - "hkdf", + "hkdf 0.12.4", "iota-logger", "iota-state", "iota-util", @@ -2048,7 +2201,7 @@ dependencies = [ "anyhow", "base64", "hex", - "hkdf", + "hkdf 0.12.4", "iota-logger", "json", "mtp", @@ -2074,19 +2227,15 @@ dependencies = [ name = "iota-util" version = "0.1.0" dependencies = [ - "aes-gcm", "base64", "hex", - "hkdf", "mtp", - "rand_core 0.6.4", + "mtp-crypto", "reqwest", - "sha2 0.10.9", "sysinfo", "tokio", "uuid", "walkdir", - "x448", "zip", ] @@ -2226,6 +2375,25 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures 0.2.17", +] + +[[package]] +name = "keccak" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", +] + [[package]] name = "lab" version = "0.11.0" @@ -2431,10 +2599,63 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "ml-dsa" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add6b9d92e496f16f4526d68ff29da1483aba4b119baeab8bed3b9e3544a6f3d" +dependencies = [ + "const-oid 0.10.2", + "crypto-common 0.2.2", + "ctutils", + "hybrid-array", + "module-lattice", + "pkcs8 0.11.0", + "shake", + "signature 3.0.0", +] + +[[package]] +name = "mlkem-rs" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b0965b8b081668ff0398dc5e9dc3f2ebb9e833393f4ab5b9f725ddce11acef8" +dependencies = [ + "rand_core 0.6.4", + "serde", + "sha3", + "subtle", + "zeroize", +] + +[[package]] +name = "mlkem-tls" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77b205d031298adf904d88efd6a57862d8650a4ab754aade19a9b5e87040bf4e" +dependencies = [ + "mlkem-rs", + "rand_core 0.6.4", + "subtle", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "module-lattice" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c61b87c9683ab7cb1c6871d261ad5479b6b10ceb52c4352aaca3b5d35a8febe" +dependencies = [ + "ctutils", + "hybrid-array", + "num-traits", +] + [[package]] name = "mtp" version = "0.1.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#15cc1d4c5e6a917f197ebf6685c8cea4e3ab668f" +source = "git+https://git.methanium.net/Methanium/mtp.git#5bfcccc056a2a491a72e4315aa95effb2243ad3c" dependencies = [ "mtp-codec", "mtp-common", @@ -2445,7 +2666,7 @@ dependencies = [ [[package]] name = "mtp-codec" version = "0.1.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#15cc1d4c5e6a917f197ebf6685c8cea4e3ab668f" +source = "git+https://git.methanium.net/Methanium/mtp.git#5bfcccc056a2a491a72e4315aa95effb2243ad3c" dependencies = [ "base64", "byteorder", @@ -2457,7 +2678,7 @@ dependencies = [ [[package]] name = "mtp-common" version = "0.1.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#15cc1d4c5e6a917f197ebf6685c8cea4e3ab668f" +source = "git+https://git.methanium.net/Methanium/mtp.git#5bfcccc056a2a491a72e4315aa95effb2243ad3c" dependencies = [ "quinn", "rustls", @@ -2465,10 +2686,27 @@ dependencies = [ "wtransport", ] +[[package]] +name = "mtp-crypto" +version = "0.1.0" +source = "git+https://git.methanium.net/Methanium/mtp.git#5bfcccc056a2a491a72e4315aa95effb2243ad3c" +dependencies = [ + "chacha20poly1305", + "ed25519-dalek", + "getrandom 0.4.3", + "hkdf 0.13.0", + "ml-dsa", + "mlkem-tls", + "rand_core 0.6.4", + "sha2 0.11.0", + "thiserror 1.0.69", + "zeroize", +] + [[package]] name = "mtp-transport" version = "0.1.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#15cc1d4c5e6a917f197ebf6685c8cea4e3ab668f" +source = "git+https://git.methanium.net/Methanium/mtp.git#5bfcccc056a2a491a72e4315aa95effb2243ad3c" dependencies = [ "log", "mtp-codec", @@ -2482,7 +2720,7 @@ dependencies = [ [[package]] name = "mtp-type-map" version = "0.1.0" -source = "git+https://git.methanium.net/Methanium/mtp.git#15cc1d4c5e6a917f197ebf6685c8cea4e3ab668f" +source = "git+https://git.methanium.net/Methanium/mtp.git#5bfcccc056a2a491a72e4315aa95effb2243ad3c" dependencies = [ "serde", "serde_yaml", @@ -2666,13 +2904,12 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "open" -version = "5.3.5" +version = "5.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fbaa89d2ddc8473c78a3adf69eea8cffa28c483b8e02a971ef31527cd0fc92c" +checksum = "cd8d3b65c44123a56e0133d2cd06ce4361bd3ca99d41198b2f25e3c3db9b8b4a" dependencies = [ "is-wsl", "libc", - "pathdiff", ] [[package]] @@ -2742,7 +2979,7 @@ dependencies = [ "futures", "futures-util", "hex", - "hkdf", + "hkdf 0.12.4", "hyper", "hyper-util", "iota-auth", @@ -2825,12 +3062,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "pathdiff" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" - [[package]] name = "pbkdf2" version = "0.12.2" @@ -2838,7 +3069,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest 0.10.7", - "hmac", + "hmac 0.12.1", ] [[package]] @@ -2851,6 +3082,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -2978,6 +3218,26 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.10", + "spki 0.7.3", +] + +[[package]] +name = "pkcs8" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7" +dependencies = [ + "der 0.8.0", + "spki 0.8.0", +] + [[package]] name = "pkg-config" version = "0.3.33" @@ -3075,6 +3335,17 @@ dependencies = [ "pnet_sys", ] +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + [[package]] name = "polyval" version = "0.6.2" @@ -3145,7 +3416,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.4", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tracing", @@ -3183,7 +3454,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.4", + "socket2 0.5.10", "tracing", "windows-sys 0.60.2", ] @@ -3232,11 +3503,11 @@ dependencies = [ [[package]] name = "rand" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" dependencies = [ - "chacha20", + "chacha20 0.10.1", "getrandom 0.4.3", "rand_core 0.10.1", ] @@ -3602,9 +3873,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" dependencies = [ "web-time", "zeroize", @@ -3843,6 +4114,27 @@ dependencies = [ "digest 0.11.3", ] +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest 0.10.7", + "keccak 0.1.6", +] + +[[package]] +name = "shake" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09057cb2149ad4cbd2da1e26b351f9a4c354219421229c69c3063e6f61947c4a" +dependencies = [ + "digest 0.11.3", + "keccak 0.2.0", + "sponge-cursor", +] + [[package]] name = "shlex" version = "2.0.1" @@ -3880,6 +4172,25 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "signature" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" +dependencies = [ + "digest 0.11.3", + "rand_core 0.10.1", +] + [[package]] name = "simd-adler32" version = "0.3.9" @@ -3940,6 +4251,32 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der 0.7.10", +] + +[[package]] +name = "spki" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f" +dependencies = [ + "base64ct", + "der 0.8.0", +] + +[[package]] +name = "sponge-cursor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a0219bd7d979d58245a4f41f695e1ac9f8befdffadd7f61f1bae9e39abc6620" + [[package]] name = "sqlite-wasm-rs" version = "0.5.5" @@ -4223,9 +4560,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.51" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" dependencies = [ "deranged", "libc", @@ -4245,9 +4582,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.30" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" +checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" dependencies = [ "num-conv", "time-core", @@ -4749,7 +5086,7 @@ dependencies = [ "futures", "futures-util", "hex", - "hkdf", + "hkdf 0.12.4", "hyper", "hyper-util", "iota-logger", @@ -5222,6 +5559,18 @@ dependencies = [ "url", ] +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + [[package]] name = "x448" version = "0.6.0" @@ -5392,7 +5741,7 @@ dependencies = [ "deflate64", "flate2", "getrandom 0.3.4", - "hmac", + "hmac 0.12.1", "indexmap", "lzma-rust2", "memchr", @@ -5407,9 +5756,9 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977347db8caa080403f6b6b7c1cda9479a8e869316f7e13a59b19076a40f94e3" +checksum = "5431d5661c32445236631278f27946e444ddafe4684cac70b185272d4f9c52d5" [[package]] name = "zmij" diff --git a/client/src/client_connection.rs b/client/src/client_connection.rs index 56bdf6a..13e03b1 100644 --- a/client/src/client_connection.rs +++ b/client/src/client_connection.rs @@ -7,8 +7,8 @@ use iota_storage::util::chats_util::{get_user, mod_user}; use iota_storage::util::communities_util::CommunitiesUtil; use iota_storage::util::config_util::CONFIG; use iota_storage::util::{chat_files, chats_util}; -use iota_util::crypto_helper; -use iota_util::crypto_util::{DataFormat, SecurePayload}; +use iota_util::crypto_helper::keyring_from_base64; +use iota_util::crypto_util::{self}; use iota_util::file_util::{get_children, load_file, save_file}; use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue}; use mtp::transport::{Receiver, Sender}; @@ -889,33 +889,18 @@ impl ClientConnection { async fn handle_challenge(&self, cv: &CommunicationValue) { let conf = CONFIG.read().await; - let private_key = conf.get_private_key().unwrap(); + let kr_str = conf.get_keyring().unwrap(); drop(conf); - let omikron_public_key = cv.get_data(DataType::PublicKey).as_str().unwrap(); - let encrypted_challenge = cv.get_data(DataType::Challenge).as_str().unwrap(); - - let solved_challenge = { - if let Ok(decrypted) = SecurePayload::new( - encrypted_challenge, - DataFormat::Base64, - crypto_helper::load_secret_key(&private_key).unwrap(), - ) { - if let Ok(decrypted) = decrypted - .decrypt_x448(crypto_helper::load_public_key(omikron_public_key).unwrap()) - { - Some(decrypted) - } else { - None - } - } else { - None - } + let Some(keyring) = keyring_from_base64(&kr_str) else { + return; }; - if let Some(decrypted) = solved_challenge { - let solved = decrypted.export(DataFormat::Raw); + let encrypted_challenge = cv.get_data(DataType::Challenge).as_str().unwrap(); + let solved = crypto_util::decrypt_challenge(encrypted_challenge, &keyring).ok(); + + if let Some(solved) = solved { let response = CommunicationValue::new(CommunicationType::ChallengeResponse) .with_id(cv.get_id()) .add_typed_default(DataType::Challenge, DataValue::Str(solved)); diff --git a/iota-cli/src/elements/console_card.rs b/iota-cli/src/elements/console_card.rs index 0de1c82..ea09717 100644 --- a/iota-cli/src/elements/console_card.rs +++ b/iota-cli/src/elements/console_card.rs @@ -3,7 +3,7 @@ use iota_logger::{log, log_command, log_cv}; use iota_state::{ACTIVE_TASKS, RELOAD, SHUTDOWN}; use iota_storage::users::{user_manager, user_profile::UserProfile}; use iota_storage::util::config_util::CONFIG; -use iota_util::{crypto_helper, file_util}; +use iota_util::file_util; use mtp::codec::{CommunicationType, CommunicationValue}; use omikron_connector::omikron_connection::OMIKRON_CONNECTION; use ratatui::{ diff --git a/iota-storage/src/users/user_manager.rs b/iota-storage/src/users/user_manager.rs index 9d0fbf1..248881a 100644 --- a/iota-storage/src/users/user_manager.rs +++ b/iota-storage/src/users/user_manager.rs @@ -1,14 +1,12 @@ use crate::users::user_profile::UserProfile; use base64::{Engine as _, engine::general_purpose::STANDARD}; -use iota_util::crypto_helper::{self}; +use iota_util::crypto_helper::{self, hex_hash, keyring_from_base64, public_key_bundle_to_base64}; use iota_util::file_util::{load_file, save_file}; use json::JsonValue; use once_cell::sync::Lazy; -use rand::Rng; -use rand_core::OsRng; +use rand_core::{OsRng, RngCore}; use std::io::{self}; use std::sync::Mutex; -use x448::{PublicKey, Secret}; static USERS: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); static UNIQUE: Lazy> = Lazy::new(|| Mutex::new(false)); @@ -20,19 +18,20 @@ pub async fn load_from_tu(username: &str) -> Result<(), ()> { let uuid = segments[0].parse::().unwrap_or(0); let b64_private_key = segments[1]; - let secret: Secret = crypto_helper::load_secret_key(b64_private_key).unwrap(); - let public_key = PublicKey::from(&secret); + let keyring = keyring_from_base64(b64_private_key).unwrap(); + let pub_key_bundle = keyring.public_key_bundle(); + let keyring_b64 = crypto_helper::keyring_to_base64(&keyring); let mut bytes = [0u8; 192]; - OsRng.fill(bytes.as_mut()); + OsRng.fill_bytes(&mut bytes); let reset_token = STANDARD.encode(&bytes); let user_profile = UserProfile::new( uuid, username.to_string(), Some(username.to_string()), - crypto_helper::public_key_to_base64(&public_key), - crypto_helper::hex_hash(b64_private_key), + public_key_bundle_to_base64(&pub_key_bundle), + hex_hash(&keyring_b64), reset_token, ); USERS.lock().unwrap().push(user_profile); diff --git a/iota-storage/src/util/config_util.rs b/iota-storage/src/util/config_util.rs index 671d2c7..2dbaada 100644 --- a/iota-storage/src/util/config_util.rs +++ b/iota-storage/src/util/config_util.rs @@ -50,6 +50,10 @@ impl ConfigUtil { self.config["port"].as_u16().unwrap_or(1984) } + pub fn get_keyring(&self) -> Option { + self.config["keyring"].as_str().map(String::from) + } + pub fn get_public_key(&self) -> Option { self.config["public_key"].as_str().map(String::from) } diff --git a/iota-util/Cargo.toml b/iota-util/Cargo.toml index 8fd86e8..e88cebf 100644 --- a/iota-util/Cargo.toml +++ b/iota-util/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] mtp = { git = "https://git.methanium.net/Methanium/mtp.git" } +mtp-crypto = { git = "https://git.methanium.net/Methanium/mtp.git", features = ["pqc"] } reqwest = "0.13.2" tokio = { version = "1.50.0", features = ["full"] } @@ -12,10 +13,5 @@ sysinfo = "0.38.3" uuid = { version = "*", features = ["v4"] } walkdir = "2.5.0" zip = "6.0.0" -aes-gcm = "0.10.3" base64 = "0.22.1" -rand_core = { version = "0.6", features = ["getrandom", "std"] } -sha2 = "0.10.9" -x448 = { version = "*" } -hkdf = "0.12.4" hex = "*" diff --git a/iota-util/src/crypto_helper.rs b/iota-util/src/crypto_helper.rs index 4a7fd76..2909c04 100644 --- a/iota-util/src/crypto_helper.rs +++ b/iota-util/src/crypto_helper.rs @@ -1,133 +1,28 @@ -use aes_gcm::{ - Aes256Gcm, Nonce, - aead::{Aead, KeyInit, OsRng}, -}; -use base64::{Engine as _, engine::general_purpose::STANDARD}; -use rand_core::RngCore; -use sha2::{Digest, Sha256}; -use x448::{PublicKey, Secret, SharedSecret}; - -/// Errors for crypto opertions -#[derive(Debug)] -#[allow(dead_code)] -pub enum CryptoError { - Base64Decode(base64::DecodeError), - InvalidKey, - AgreementError, - EncryptionError(aes_gcm::Error), - DecryptionError(aes_gcm::Error), -} - -impl From for CryptoError { - fn from(err: base64::DecodeError) -> Self { - CryptoError::Base64Decode(err) - } -} - -pub struct KeyPair { - pub secret: Secret, - pub public: PublicKey, -} - -pub fn generate_keypair() -> KeyPair { - let mut buf = [0u8; 56]; - let mut rng = OsRng; - rng.fill_bytes(&mut buf); - let secret = Secret::from_bytes(&buf).unwrap(); - let public = PublicKey::from(&secret); - KeyPair { secret, public } -} - -pub fn public_key_to_base64(pubkey: &PublicKey) -> String { - STANDARD.encode(pubkey.as_bytes().as_ref()) -} - -pub fn secret_key_to_base64(secret: &Secret) -> String { - STANDARD.encode(secret.as_bytes().as_ref()) -} - -pub fn load_public_key(base64_pub: &str) -> Option { - let bytes = STANDARD.decode(base64_pub).unwrap(); - PublicKey::from_bytes(&bytes) -} - -pub fn load_secret_key(base64_secret: &str) -> Option { - let bytes = STANDARD.decode(base64_secret).unwrap(); - Secret::from_bytes(&bytes) -} - -#[allow(dead_code)] -fn derive_aes_key(shared: &SharedSecret) -> [u8; 32] { - let mut hasher = Sha256::new(); - hasher.update(shared.as_bytes()); - let result = hasher.finalize(); - let mut key = [0u8; 32]; - key.copy_from_slice(&result[..32]); - key -} - -#[allow(dead_code)] -pub fn encrypt( - base64_secret: &str, - base64_peer_pub: &str, - plaintext: &str, -) -> Result { - let secret = load_secret_key(base64_secret).unwrap(); - let peer_pub = load_public_key(base64_peer_pub).unwrap(); - let shared = secret - .to_diffie_hellman(&peer_pub) - .ok_or(CryptoError::AgreementError)?; - let key_bytes = derive_aes_key(&shared); - let cipher = Aes256Gcm::new_from_slice(&key_bytes).expect("Key length should be correct"); - let mut nonce_bytes = [0u8; 12]; - OsRng.fill_bytes(&mut nonce_bytes); - let nonce = Nonce::from_slice(&nonce_bytes); - let ciphertext = cipher - .encrypt(nonce, plaintext.as_bytes()) - .map_err(CryptoError::EncryptionError)?; - // prefix nonce to ciphertext - let mut out = Vec::with_capacity(nonce_bytes.len() + ciphertext.len()); - out.extend_from_slice(&nonce_bytes); - out.extend_from_slice(&ciphertext); - Ok(STANDARD.encode(&out)) -} - -#[allow(dead_code)] -pub fn decrypt( - base64_secret: &str, - base64_peer_pub: &str, - encrypted_base64: &str, -) -> Result { - let secret = load_secret_key(base64_secret).unwrap(); - let peer_pub = load_public_key(base64_peer_pub).unwrap(); - let shared = secret - .to_diffie_hellman(&peer_pub) - .ok_or(CryptoError::AgreementError)?; - let key_bytes = derive_aes_key(&shared); - let cipher = Aes256Gcm::new_from_slice(&key_bytes).expect("Key length should be correct"); - - let encrypted = STANDARD.decode(encrypted_base64)?; - if encrypted.len() < 12 { - return Err(CryptoError::DecryptionError(aes_gcm::Error)); - } - let nonce_bytes = &encrypted[..12]; - let ciphertext = &encrypted[12..]; - let nonce = Nonce::from_slice(nonce_bytes); - let plaintext_bytes = cipher - .decrypt(nonce, ciphertext) - .map_err(CryptoError::DecryptionError)?; - let plaintext = String::from_utf8(plaintext_bytes) - .map_err(|_| CryptoError::DecryptionError(aes_gcm::Error))?; - Ok(plaintext) -} - -pub fn hash_it(input: &str) -> Vec { - let mut hasher = Sha256::new(); - hasher.update(input.as_bytes()); - hasher.finalize().to_vec() -} - -pub fn hex_hash(input: &str) -> String { - let digest = hash_it(input); - digest.iter().map(|b| format!("{:02x}", b)).collect() -} +use base64::{Engine as _, engine::general_purpose::STANDARD}; +use mtp_crypto::{Keyring, PublicKeyBundle}; + +pub fn generate_keyring() -> Keyring { + Keyring::generate() +} + +pub fn keyring_to_base64(keyring: &Keyring) -> String { + STANDARD.encode(keyring.to_bytes()) +} + +pub fn keyring_from_base64(s: &str) -> Option { + let bytes = STANDARD.decode(s).ok()?; + Keyring::from_bytes(&bytes).ok() +} + +pub fn public_key_bundle_to_base64(bundle: &PublicKeyBundle) -> String { + STANDARD.encode(bundle.as_bytes()) +} + +pub fn public_key_bundle_from_base64(s: &str) -> Option { + let bytes = STANDARD.decode(s).ok()?; + PublicKeyBundle::from_bytes(&bytes).ok() +} + +pub fn hex_hash(input: &str) -> String { + hex::encode(mtp_crypto::sha256(input.as_bytes())) +} diff --git a/iota-util/src/crypto_util.rs b/iota-util/src/crypto_util.rs index bb784e8..defc41c 100644 --- a/iota-util/src/crypto_util.rs +++ b/iota-util/src/crypto_util.rs @@ -1,178 +1,58 @@ -use aes_gcm::{ - Aes256Gcm, Nonce, - aead::{Aead, KeyInit, Payload}, -}; -use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64_STD}; -use hkdf::Hkdf; -type HkdfSha256 = sha2::Sha256; -use sha2::{Digest, Sha256 as HashSha256}; -use x448::{PublicKey, Secret}; - -#[derive(Debug)] -#[allow(dead_code)] -pub enum SecurePayloadError { - InvalidBase64, - InvalidHex, - EncryptionError, - DecryptionError, - InvalidKeyLength, -} +use base64::{Engine as _, engine::general_purpose::STANDARD}; +use mtp_crypto::{EncryptionType, Keyring, PublicKeyBundle, encrypt_for, decrypt_with}; #[derive(Clone, Copy, Debug)] -#[allow(dead_code)] pub enum DataFormat { Raw, Base64, Hex, } -pub struct SecurePayload { - inner_data: Vec, - private_key: Secret, +pub fn encrypt( + plaintext: &[u8], + aad: &[u8], + recipient_pub_key_bundle: &PublicKeyBundle, +) -> Result, String> { + encrypt_for( + EncryptionType::MlKemChaCha20Poly1305, + recipient_pub_key_bundle, + plaintext, + aad, + ) + .map_err(|e| format!("encryption error: {:?}", e)) } -impl Clone for SecurePayload { - fn clone(&self) -> Self { - Self { - inner_data: self.inner_data.clone(), - private_key: Secret::from_bytes(self.private_key.as_bytes()).unwrap(), - } - } +pub fn decrypt( + ciphertext: &[u8], + aad: &[u8], + keyring: &Keyring, +) -> Result, String> { + decrypt_with(ciphertext, keyring, aad) + .map_err(|e| format!("decryption error: {:?}", e)) } -#[allow(dead_code)] -impl SecurePayload { - pub fn new>( - data: T, - format: DataFormat, - private_key: S, - ) -> Result - where - S: Into, - { - let raw_data = match format { - DataFormat::Raw => data.as_ref().to_vec(), - DataFormat::Base64 => BASE64_STD - .decode(data.as_ref()) - .map_err(|_| SecurePayloadError::InvalidBase64)?, - DataFormat::Hex => { - hex::decode(data.as_ref()).map_err(|_| SecurePayloadError::InvalidHex)? - } - }; +pub fn encrypt_challenge( + challenge: &str, + recipient_pub_key_bundle: &PublicKeyBundle, +) -> Result { + let blob = encrypt(challenge.as_bytes(), b"challenge", recipient_pub_key_bundle)?; + Ok(STANDARD.encode(&blob)) +} - Ok(Self { - inner_data: raw_data, - private_key: private_key.into(), - }) - } +pub fn decrypt_challenge( + encrypted: &str, + keyring: &Keyring, +) -> Result { + let blob = + STANDARD.decode(encrypted).map_err(|e| format!("base64 decode error: {}", e))?; + let pt = decrypt(&blob, b"challenge", keyring)?; + String::from_utf8(pt).map_err(|e| format!("utf8 decode error: {}", e)) +} - pub fn get_public_key(&self) -> [u8; 56] { - *PublicKey::from(&self.private_key).as_bytes() - } - - pub fn export(&self, format: DataFormat) -> String { - match format.into() { - DataFormat::Raw => String::from_utf8_lossy(&self.inner_data).to_string(), - DataFormat::Base64 => BASE64_STD.encode(&self.inner_data), - DataFormat::Hex => hex::encode(&self.inner_data), - } - } - - pub fn get_bytes(&self) -> &[u8] { - &self.inner_data - } - - pub fn get_hash(&self, format: DataFormat) -> String { - let mut hasher = HashSha256::new(); - hasher.update(&self.inner_data); - let result = hasher.finalize(); - - match format { - DataFormat::Raw => String::from_utf8_lossy(&result).to_string(), - DataFormat::Base64 => BASE64_STD.encode(result), - DataFormat::Hex => hex::encode(result), - } - } - - pub fn encrypt_x448(&self, public_key: S) -> Result - where - S: Into, - { - let peer_pub = public_key.into(); - let shared_secret = self.private_key.as_diffie_hellman(&peer_pub).unwrap(); - - let hkdf = Hkdf::::new(None, shared_secret.as_bytes()); - let mut okm = [0u8; 44]; - - hkdf.expand(b"x448-aes-gcm-no-overhead", &mut okm) - .map_err(|_| SecurePayloadError::EncryptionError)?; - - let key = &okm[..32]; - let nonce_bytes = &okm[32..]; - - let cipher = Aes256Gcm::new(key.into()); - let nonce = Nonce::from_slice(nonce_bytes); - - let ciphertext = cipher - .encrypt( - nonce, - Payload { - msg: &self.inner_data, - aad: &[], - }, - ) - .map_err(|_| SecurePayloadError::EncryptionError)?; - - Ok(SecurePayload { - inner_data: ciphertext, - private_key: Secret::from_bytes(self.private_key.as_bytes()).unwrap(), - }) - } - - pub fn decrypt_to_format( - &self, - peer_public_key_bytes: &[u8; 56], - output_format: DataFormat, - ) -> Result { - let decrypted_instance = - self.decrypt_x448(PublicKey::from_bytes(peer_public_key_bytes).unwrap())?; - Ok(decrypted_instance.export(output_format)) - } - - pub fn decrypt_x448( - &self, - peer_public_key_bytes: S, - ) -> Result - where - S: Into, - { - let peer_pub = peer_public_key_bytes.into(); - let shared_secret = self.private_key.as_diffie_hellman(&peer_pub).unwrap(); - - let hkdf = Hkdf::::new(None, shared_secret.as_bytes()); - let mut okm = [0u8; 44]; - hkdf.expand(b"x448-aes-gcm-no-overhead", &mut okm) - .map_err(|_| SecurePayloadError::DecryptionError)?; - - let key = &okm[..32]; - let nonce_bytes = &okm[32..]; - - let cipher = Aes256Gcm::new(key.into()); - let nonce = Nonce::from_slice(nonce_bytes); - - let plaintext = cipher - .decrypt( - nonce, - Payload { - msg: &self.inner_data, - aad: &[], - }, - ) - .map_err(|_| SecurePayloadError::DecryptionError)?; - - Ok(SecurePayload { - inner_data: plaintext, - private_key: Secret::from_bytes(self.private_key.as_bytes()).unwrap(), - }) +pub fn export(data: &[u8], format: DataFormat) -> String { + match format { + DataFormat::Raw => String::from_utf8_lossy(data).to_string(), + DataFormat::Base64 => STANDARD.encode(data), + DataFormat::Hex => hex::encode(data), } } diff --git a/omikron-connector/src/omikron_connection.rs b/omikron-connector/src/omikron_connection.rs index 1dbc346..e77c8c3 100755 --- a/omikron-connector/src/omikron_connection.rs +++ b/omikron-connector/src/omikron_connection.rs @@ -6,8 +6,8 @@ use iota_storage::util::chat_files::{self, MessageState, change_message_state}; use iota_storage::util::chats_util::{self, get_user, mod_user}; use iota_storage::util::communities_util::CommunitiesUtil; use iota_storage::util::config_util::CONFIG; -use iota_util::crypto_helper; -use iota_util::crypto_util::{DataFormat, SecurePayload}; +use iota_util::crypto_helper::{self, keyring_from_base64}; +use iota_util::crypto_util::{self}; use iota_util::file_util::{get_children, has_file, load_file, save_file}; use json::JsonValue; use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue}; @@ -347,30 +347,41 @@ impl OmikronConnection { async fn handle_authentication(&self) { let conf = CONFIG.read().await; let iota_id = conf.get_iota_id(); - let public_key = conf.get_public_key(); - let private_key = conf.get_private_key(); + let keyring_b64 = conf.get_keyring(); drop(conf); if iota_id == 0 { log_t!("iota_register_new"); - let (pub_k, _priv_k) = if let (Some(pk), Some(sk)) = (public_key, private_key) { - (pk, sk) + let pub_key_b64 = if let Some(kr) = keyring_b64 { + if let Some(keyring) = keyring_from_base64(&kr) { + let bundle = keyring.public_key_bundle(); + crypto_helper::public_key_bundle_to_base64(&bundle) + } else { + let keyring = crypto_helper::generate_keyring(); + let kb64 = crypto_helper::keyring_to_base64(&keyring); + let bundle = keyring.public_key_bundle(); + let pk_b64 = crypto_helper::public_key_bundle_to_base64(&bundle); + let mut conf_write = CONFIG.write().await; + conf_write.change("keyring", JsonValue::from(kb64)); + conf_write.update(); + drop(conf_write); + pk_b64 + } } else { - let key_pair = crypto_helper::generate_keypair(); - let public_key_base64 = crypto_helper::public_key_to_base64(&key_pair.public); - let private_key_base64 = crypto_helper::secret_key_to_base64(&key_pair.secret); - + let keyring = crypto_helper::generate_keyring(); + let kb64 = crypto_helper::keyring_to_base64(&keyring); + let bundle = keyring.public_key_bundle(); + let pk_b64 = crypto_helper::public_key_bundle_to_base64(&bundle); let mut conf_write = CONFIG.write().await; - conf_write.change("public_key", JsonValue::from(public_key_base64.clone())); - conf_write.change("private_key", JsonValue::from(private_key_base64.clone())); + conf_write.change("keyring", JsonValue::from(kb64)); conf_write.update(); drop(conf_write); - (public_key_base64, private_key_base64) + pk_b64 }; let register_msg = CommunicationValue::new(CommunicationType::RegisterIota) - .add_typed_default(DataType::PublicKey, DataValue::Str(pub_k)); + .add_typed_default(DataType::PublicKey, DataValue::Str(pub_key_b64)); let msg_id = register_msg.get_id(); @@ -522,8 +533,6 @@ impl OmikronConnection { } if trusted { - use iota_util::crypto_util::{DataFormat, SecurePayload}; - let challenge = Uuid::new_v4().to_string(); self.app_challenges @@ -535,31 +544,36 @@ impl OmikronConnection { .await .insert(sender_id, (user_id, app_identifier.clone())); - if let Some(pub_key) = iota_util::crypto_helper::load_public_key(&app_public_key) { + if let Some(app_pub_bundle) = + iota_util::crypto_helper::public_key_bundle_from_base64(&app_public_key) + { let conf = CONFIG.read().await; - let priv_k_str = conf.get_private_key().unwrap_or_default(); - let pub_k_str = conf.get_public_key().unwrap_or_default(); + let kr_str = conf.get_keyring().unwrap_or_default(); drop(conf); - if let Some(priv_key) = iota_util::crypto_helper::load_secret_key(&priv_k_str) { - let encrypted_challenge = - SecurePayload::new(challenge.as_bytes(), DataFormat::Raw, priv_key) - .unwrap() - .encrypt_x448(pub_key) - .unwrap() - .export(DataFormat::Base64); + if let Some(keyring) = keyring_from_base64(&kr_str) { + if let Ok(encrypted_challenge) = crypto_util::encrypt_challenge( + &challenge, + &app_pub_bundle, + ) { + let bundle = keyring.public_key_bundle(); + let pub_k_b64 = crypto_helper::public_key_bundle_to_base64(&bundle); - let res = CommunicationValue::new(CommunicationType::AppChallenge) - .with_id(cv.get_id()) - .with_receiver(sender_id) - .add_typed_default(DataType::PublicKey, DataValue::Str(pub_k_str)) - .add_typed_default( - DataType::Challenge, - DataValue::Str(encrypted_challenge), - ); + let res = CommunicationValue::new(CommunicationType::AppChallenge) + .with_id(cv.get_id()) + .with_receiver(sender_id) + .add_typed_default( + DataType::PublicKey, + DataValue::Str(pub_k_b64), + ) + .add_typed_default( + DataType::Challenge, + DataValue::Str(encrypted_challenge), + ); - self.send_message(&res).await; - return; + self.send_message(&res).await; + return; + } } } } @@ -1686,17 +1700,17 @@ impl OmikronConnection { async fn handle_challenge(&self, cv: &CommunicationValue) { let conf = CONFIG.read().await; - let Some(private_key) = conf.get_private_key() else { + let Some(kr_str) = conf.get_keyring() else { drop(conf); log_t!("omikron_challenge_decryption_failed"); *self.auth_failure.write().await = Some( - "Challenge decryption failed: no private key configured on this Iota.".to_string(), + "Challenge decryption failed: no keyring configured on this Iota.".to_string(), ); return; }; drop(conf); - let Some(omikron_public_key) = cv.get_data(DataType::PublicKey).as_str() else { + let Some(_omikron_pub_key_bundle) = cv.get_data(DataType::PublicKey).as_str() else { log_t!("omikron_challenge_decryption_failed"); return; }; @@ -1705,23 +1719,14 @@ impl OmikronConnection { return; }; - let Some(secret_key) = crypto_helper::load_secret_key(&private_key) else { - log_t!("omikron_challenge_decryption_failed"); - return; - }; - let Some(pub_key) = crypto_helper::load_public_key(omikron_public_key) else { + let Some(keyring) = keyring_from_base64(&kr_str) else { log_t!("omikron_challenge_decryption_failed"); return; }; - let solved_challenge = - SecurePayload::new(encrypted_challenge, DataFormat::Base64, secret_key) - .ok() - .and_then(|decrypted| decrypted.decrypt_x448(pub_key).ok()); - - if let Some(decrypted) = solved_challenge { - let solved = decrypted.export(DataFormat::Raw); + let solved_challenge = crypto_util::decrypt_challenge(encrypted_challenge, &keyring).ok(); + if let Some(solved) = solved_challenge { let response = CommunicationValue::new(CommunicationType::ChallengeResponse) .with_id(cv.get_id()) .add_typed_default(DataType::Challenge, DataValue::Str(solved)); @@ -1730,7 +1735,7 @@ impl OmikronConnection { } else { log_t!("omikron_challenge_decryption_failed"); *self.auth_failure.write().await = Some( - "Challenge decryption failed — your Iota private key may not match the registered key on the server." + "Challenge decryption failed — your Iota keyring may not match the registered keys on the server." .to_string(), ); } diff --git a/omikron-connector/src/user_ops.rs b/omikron-connector/src/user_ops.rs index 978e742..2d98042 100644 --- a/omikron-connector/src/user_ops.rs +++ b/omikron-connector/src/user_ops.rs @@ -1,16 +1,13 @@ use base64::{Engine as _, engine::general_purpose::STANDARD}; -use hex; use iota_logger::{PrintType, log, log_cv, log_t}; use iota_state::{RELOAD, SHUTDOWN}; use iota_storage::users::user_manager::{add_user, save_users}; use iota_storage::users::user_profile::UserProfile; -use iota_util::crypto_helper::public_key_to_base64; +use iota_util::crypto_helper::{self, hex_hash, public_key_bundle_to_base64}; use iota_util::file_util::save_file; use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue}; use rand_core::{OsRng, RngCore}; -use sha2::{Digest, Sha256}; use std::time::Duration; -use x448::{PublicKey, Secret}; use crate::omikron_connection::OMIKRON_CONNECTION; @@ -41,16 +38,11 @@ pub async fn create_user(username: &str) -> (Option, Option return (None, None); } }; - let mut buffer = [0u8; 56]; - let mut rng = OsRng; - rng.fill_bytes(&mut buffer); - let private_key = Secret::from_bytes(&buffer).unwrap(); - let public_key = PublicKey::from(&private_key); + let keyring = crypto_helper::generate_keyring(); + let pub_key_bundle = keyring.public_key_bundle(); + let keyring_b64 = crypto_helper::keyring_to_base64(&keyring); - let mut hasher = Sha256::new(); - hasher.update(&STANDARD.encode(&private_key.as_bytes()).as_bytes()); - let result = hasher.finalize(); - let private_key_hash = hex::encode(result); + let private_key_hash = hex_hash(&keyring_b64); let mut bytes = [0u8; 192]; OsRng.fill_bytes(&mut bytes); @@ -60,7 +52,7 @@ pub async fn create_user(username: &str) -> (Option, Option user_id as i64, username.to_string(), None, - STANDARD.encode(&public_key.as_bytes()), + public_key_bundle_to_base64(&pub_key_bundle), private_key_hash, reset_token.clone(), ); @@ -70,7 +62,7 @@ pub async fn create_user(username: &str) -> (Option, Option .add_typed_default(DataType::Username, DataValue::Str(username.to_string())) .add_typed_default( DataType::PublicKey, - DataValue::Str(public_key_to_base64(&public_key)), + DataValue::Str(public_key_bundle_to_base64(&pub_key_bundle)), ) .add_typed_default(DataType::IotaId, DataValue::SignedNumber(user_id as i128)) .add_typed_default(DataType::ResetToken, DataValue::Str(reset_token)); @@ -94,13 +86,13 @@ pub async fn create_user(username: &str) -> (Option, Option save_file( "", &format!("{}.tu", username), - &format!("{}::{}", user_id, STANDARD.encode(&private_key.as_bytes())), + &format!("{}::{}", user_id, keyring_b64), ); add_user(user_profile.clone()); save_users(); ( Some(user_profile), - Some(STANDARD.encode(&private_key.as_bytes())), + Some(keyring_b64), ) }