diff --git a/Cargo.lock b/Cargo.lock index c1b891707..0139892f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,28 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[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-prng" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49cccd49cb7034d6ee7db9ac3549bb3fb38ff17179d93b726efb974cc9ddafa9" +dependencies = [ + "aes", + "byteorder", + "rand", +] + [[package]] name = "ahash" version = "0.8.11" @@ -127,13 +149,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -176,7 +198,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.1.0", + "fastrand 2.1.1", "hex", "http 0.2.12", "ring", @@ -189,9 +211,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16838e6c9e12125face1c1eff1343c75e3ff540de98ff7ebd61874a89bcfeb9" +checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -201,20 +223,21 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f42c2d4218de4dcd890a109461e2f799a1a2ba3bcd2cde9af88360f5df9266c6" +checksum = "2424565416eef55906f9f8cece2072b6b6a76075e3ff81483ebe938a89a4c05f" dependencies = [ "aws-credential-types", "aws-sigv4", "aws-smithy-async", "aws-smithy-eventstream", "aws-smithy-http", + "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.1.0", + "fastrand 2.1.1", "http 0.2.12", "http-body 0.4.6", "once_cell", @@ -226,9 +249,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ebbbc319551583b9233a74b359ede7349102e779fc12371d2478e80b50d218" +checksum = "178910fefe72743b62b9c4670c14a038ebfdb265ff7feccf43827af6a8899e14" dependencies = [ "aws-credential-types", "aws-runtime", @@ -248,9 +271,9 @@ dependencies = [ [[package]] name = "aws-sdk-s3" -version = "1.46.0" +version = "1.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abf69a87be33b6f125a93d5046b5f7395c26d1f449bf8d3927f5577463b6de0" +checksum = "cca49303c05d2a740b8a4552fac63a4db6ead84f7e7eeed04761fd3014c26f25" dependencies = [ "ahash", "aws-credential-types", @@ -267,7 +290,7 @@ dependencies = [ "aws-smithy-xml", "aws-types", "bytes", - "fastrand 2.1.0", + "fastrand 2.1.1", "hex", "hmac", "http 0.2.12", @@ -283,9 +306,9 @@ dependencies = [ [[package]] name = "aws-sdk-secretsmanager" -version = "1.43.0" +version = "1.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f468d566c05086b1b6a08e9de12dca141071a717580dc075f180d0fe11b6190f" +checksum = "2039325a02aa048e510442bb006129ec64c0c775666db905c315d2e714aeb2c0" dependencies = [ "aws-credential-types", "aws-runtime", @@ -297,7 +320,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.1.0", + "fastrand 2.1.1", "http 0.2.12", "once_cell", "regex-lite", @@ -306,9 +329,9 @@ dependencies = [ [[package]] name = "aws-sdk-sns" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab2b9787b8d9d3094ace9585e785079cfc583199ec620ab067b599e8850c1a6" +checksum = "e1cd040b8df17f8155c808ebebef9652939180974e1cafd0f56e8ac7422f7c82" dependencies = [ "aws-credential-types", "aws-runtime", @@ -329,9 +352,9 @@ dependencies = [ [[package]] name = "aws-sdk-sqs" -version = "1.39.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dee5603f3843d25559e670bed478c613ad0feb489db8e20f92d1e51227c3b58" +checksum = "6e3bca5c1ddd1b8f3d936c5d51ec32ca327d080bdfe908d46a3a32cf8fdcf30e" dependencies = [ "aws-credential-types", "aws-runtime", @@ -351,9 +374,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.39.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11822090cf501c316c6f75711d77b96fba30658e3867a7762e5e2f5d32d31e81" +checksum = "e5879bec6e74b648ce12f6085e7245417bc5f6d672781028384d2e494be3eb6d" dependencies = [ "aws-credential-types", "aws-runtime", @@ -373,9 +396,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78a2a06ff89176123945d1bbe865603c4d7101bea216a550bb4d2e4e9ba74d74" +checksum = "4ef4cd9362f638c22a3b959fd8df292e7e47fdf170270f86246b97109b5f2f7d" dependencies = [ "aws-credential-types", "aws-runtime", @@ -395,9 +418,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.39.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20a91795850826a6f456f4a48eff1dfa59a0e69bdbf5b8c50518fd372106574" +checksum = "0b1e2735d2ab28b35ecbb5496c9d41857f52a0d6a0075bbf6a8af306045ea6f6" dependencies = [ "aws-credential-types", "aws-runtime", @@ -490,9 +513,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.60.9" +version = "0.60.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9cd0ae3d97daa0a2bf377a4d8e8e1362cae590c4a1aad0d40058ebca18eb91e" +checksum = "01dbcb6e2588fd64cfb6d7529661b06466419e4c54ed1c62d6510d2d0350a728" dependencies = [ "aws-smithy-eventstream", "aws-smithy-runtime-api", @@ -530,16 +553,16 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.6.3" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0abbf454960d0db2ad12684a1640120e7557294b0ff8e2f11236290a1b293225" +checksum = "d1ce695746394772e7000b39fe073095db6d45a862d0767dd5ad0ac0d7f8eb87" dependencies = [ "aws-smithy-async", "aws-smithy-http", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", - "fastrand 2.1.0", + "fastrand 2.1.1", "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", @@ -574,9 +597,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.2" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cee7cadb433c781d3299b916fbf620fea813bf38f49db282fb6858141a05cc8" +checksum = "273dcdfd762fae3e1650b8024624e7cd50e484e37abdab73a7a706188ad34543" dependencies = [ "base64-simd", "bytes", @@ -617,7 +640,7 @@ dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", "aws-smithy-types", - "rustc_version 0.4.0", + "rustc_version 0.4.1", "tracing", ] @@ -796,9 +819,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31" +checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2" dependencies = [ "bytemuck_derive", ] @@ -811,7 +834,7 @@ checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -853,9 +876,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.13" +version = "1.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" +checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" dependencies = [ "shlex", ] @@ -907,6 +930,16 @@ dependencies = [ "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.16" @@ -938,7 +971,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -1073,7 +1106,7 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" dependencies = [ - "rustc_version 0.4.0", + "rustc_version 0.4.1", ] [[package]] @@ -1319,19 +1352,15 @@ dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", - "rustc_version 0.4.0", - "syn 2.0.75", + "rustc_version 0.4.1", + "syn 2.0.77", ] [[package]] name = "diatomic-waker" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce3b3a843da2390ad9693fc640ed7081015fd73f280cab2b2081404a8ecceb99" -dependencies = [ - "loom", - "waker-fn", -] +checksum = "af873b6853650fb206431c52fa7bbf6917146b70a8a9979d6d141f5d5394086b" [[package]] name = "digest" @@ -1512,9 +1541,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "ff" @@ -1687,7 +1716,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -1720,19 +1749,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generator" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "979f00864edc7516466d6b3157706e06c032f22715700ddd878228a91d02bc56" -dependencies = [ - "cfg-if", - "libc", - "log", - "rustversion", - "windows", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -2146,7 +2162,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core 0.52.0", + "windows-core", ] [[package]] @@ -2193,9 +2209,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -2220,6 +2236,15 @@ version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.13" @@ -2318,6 +2343,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "iris-mpc-cpu" +version = "0.1.0" +dependencies = [ + "aes-prng", + "bytemuck", + "bytes", + "eyre", + "futures", + "iris-mpc-common", + "num-traits", + "rand", + "rayon", + "serde", + "static_assertions", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "iris-mpc-gpu" version = "0.1.0" @@ -2562,19 +2607,6 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" -[[package]] -name = "loom" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "tracing", - "tracing-subscriber", -] - [[package]] name = "lru" version = "0.12.4" @@ -2705,7 +2737,7 @@ checksum = "38b4faf00617defe497754acde3024865bc143d44a86799b24e191ecff91354f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -2977,9 +3009,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.36.3" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "memchr", ] @@ -3019,7 +3051,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -3265,7 +3297,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -3296,7 +3328,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -3438,9 +3470,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -3668,7 +3700,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sync_wrapper 1.0.1", - "system-configuration 0.6.0", + "system-configuration 0.6.1", "tokio", "tokio-native-tls", "tower-service", @@ -3785,9 +3817,9 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver 1.0.23", ] @@ -3804,9 +3836,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" dependencies = [ "bitflags 2.6.0", "errno", @@ -3835,7 +3867,7 @@ checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ "once_cell", "rustls-pki-types", - "rustls-webpki 0.102.6", + "rustls-webpki 0.102.7", "subtle", "zeroize", ] @@ -3889,9 +3921,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.102.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" dependencies = [ "ring", "rustls-pki-types", @@ -3928,12 +3960,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -4010,9 +4036,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.208" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] @@ -4037,20 +4063,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.208" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ "indexmap", "itoa", @@ -4528,9 +4554,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.75" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -4565,9 +4591,9 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.6.0", "core-foundation", @@ -4641,7 +4667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", - "fastrand 2.1.0", + "fastrand 2.1.1", "once_cell", "rustix", "windows-sys 0.59.0", @@ -4664,7 +4690,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -4744,9 +4770,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.3" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", @@ -4768,7 +4794,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -4921,7 +4947,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -5235,7 +5261,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", "wasm-bindgen-shared", ] @@ -5269,7 +5295,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5353,16 +5379,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows-core" version = "0.52.0" @@ -5372,41 +5388,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-strings", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-implement" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.75", -] - -[[package]] -name = "windows-interface" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.75", -] - [[package]] name = "windows-registry" version = "0.2.0" @@ -5670,7 +5651,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e0de04056..2bf1dc80c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "iris-mpc", + "iris-mpc-cpu", "iris-mpc-gpu", "iris-mpc-common", "iris-mpc-upgrade", diff --git a/iris-mpc-cpu/Cargo.toml b/iris-mpc-cpu/Cargo.toml new file mode 100644 index 000000000..d4739a0c2 --- /dev/null +++ b/iris-mpc-cpu/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "iris-mpc-cpu" +version = "0.1.0" +edition = "2021" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + + +[dependencies] +aes-prng = "0.2" +bytes = "1.7" +bytemuck.workspace = true +eyre.workspace = true +futures.workspace = true +iris-mpc-common = { path = "../iris-mpc-common" } +num-traits = "0.2" +rand.workspace = true +rayon.workspace = true +serde.workspace = true +static_assertions.workspace = true +thiserror = "1.0" +tokio.workspace = true +tracing.workspace = true diff --git a/iris-mpc-cpu/src/lib.rs b/iris-mpc-cpu/src/lib.rs new file mode 100644 index 000000000..e8b0ee98d --- /dev/null +++ b/iris-mpc-cpu/src/lib.rs @@ -0,0 +1,5 @@ +pub(crate) mod networks; +pub mod prelude; +pub(crate) mod protocol; +pub(crate) mod shares; +pub(crate) mod utils; diff --git a/iris-mpc-cpu/src/networks/mod.rs b/iris-mpc-cpu/src/networks/mod.rs new file mode 100644 index 000000000..6274c758e --- /dev/null +++ b/iris-mpc-cpu/src/networks/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod network_trait; +pub(crate) mod test_network; diff --git a/iris-mpc-cpu/src/networks/network_trait.rs b/iris-mpc-cpu/src/networks/network_trait.rs new file mode 100644 index 000000000..e7ca1e7cf --- /dev/null +++ b/iris-mpc-cpu/src/networks/network_trait.rs @@ -0,0 +1,42 @@ +use bytes::{Bytes, BytesMut}; +use eyre::{Error, Result}; +use iris_mpc_common::id::PartyID; +pub type IoError = std::io::Error; + +#[allow(async_fn_in_trait)] +pub trait NetworkTrait: Send + Sync { + fn get_id(&self) -> PartyID; + + async fn shutdown(self) -> Result<(), IoError>; + + async fn send(&mut self, id: PartyID, data: Bytes) -> Result<(), IoError>; + async fn send_next_id(&mut self, data: Bytes) -> Result<(), IoError>; + async fn send_prev_id(&mut self, data: Bytes) -> Result<(), IoError>; + + async fn receive(&mut self, id: PartyID) -> Result; + async fn receive_prev_id(&mut self) -> Result; + async fn receive_next_id(&mut self) -> Result; + + async fn broadcast(&mut self, data: Bytes) -> Result, IoError>; + //======= sync world ========= + fn blocking_send(&mut self, id: PartyID, data: Bytes) -> Result<(), IoError>; + fn blocking_send_next_id(&mut self, data: Bytes) -> Result<(), IoError>; + fn blocking_send_prev_id(&mut self, data: Bytes) -> Result<(), IoError>; + + fn blocking_receive(&mut self, id: PartyID) -> Result; + fn blocking_receive_prev_id(&mut self) -> Result; + fn blocking_receive_next_id(&mut self) -> Result; + + fn blocking_broadcast(&mut self, data: Bytes) -> Result, IoError>; +} + +#[allow(async_fn_in_trait)] +pub trait NetworkEstablisher { + fn get_id(&self) -> PartyID; + fn get_num_parties(&self) -> usize; + async fn open_channel(&mut self) -> Result; + async fn shutdown(self) -> Result<(), Error>; + //======= sync world ========= + fn print_connection_stats(&self, out: &mut impl std::io::Write) -> std::io::Result<()>; + fn get_send_receive(&self, i: usize) -> std::io::Result<(u64, u64)>; +} diff --git a/iris-mpc-cpu/src/networks/test_network.rs b/iris-mpc-cpu/src/networks/test_network.rs new file mode 100644 index 000000000..15c310256 --- /dev/null +++ b/iris-mpc-cpu/src/networks/test_network.rs @@ -0,0 +1,358 @@ +use super::network_trait::{NetworkEstablisher, NetworkTrait}; +use bytes::{Bytes, BytesMut}; +use eyre::{eyre, Error}; +use iris_mpc_common::id::PartyID; +use std::{ + collections::VecDeque, + io, + io::{Error as IOError, ErrorKind as IOErrorKind}, +}; +use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; + +pub struct TestNetwork3p { + p1_p2_sender: UnboundedSender, + p1_p3_sender: UnboundedSender, + p2_p3_sender: UnboundedSender, + p2_p1_sender: UnboundedSender, + p3_p1_sender: UnboundedSender, + p3_p2_sender: UnboundedSender, + p1_p2_receiver: UnboundedReceiver, + p1_p3_receiver: UnboundedReceiver, + p2_p3_receiver: UnboundedReceiver, + p2_p1_receiver: UnboundedReceiver, + p3_p1_receiver: UnboundedReceiver, + p3_p2_receiver: UnboundedReceiver, +} + +impl Default for TestNetwork3p { + fn default() -> Self { + Self::new() + } +} + +impl TestNetwork3p { + pub fn new() -> Self { + // AT Most 1 message is buffered before they are read so this should be fine + let p1_p2 = mpsc::unbounded_channel(); + let p1_p3 = mpsc::unbounded_channel(); + let p2_p3 = mpsc::unbounded_channel(); + let p2_p1 = mpsc::unbounded_channel(); + let p3_p1 = mpsc::unbounded_channel(); + let p3_p2 = mpsc::unbounded_channel(); + + Self { + p1_p2_sender: p1_p2.0, + p1_p3_sender: p1_p3.0, + p2_p1_sender: p2_p1.0, + p2_p3_sender: p2_p3.0, + p3_p1_sender: p3_p1.0, + p3_p2_sender: p3_p2.0, + p1_p2_receiver: p1_p2.1, + p1_p3_receiver: p1_p3.1, + p2_p1_receiver: p2_p1.1, + p2_p3_receiver: p2_p3.1, + p3_p1_receiver: p3_p1.1, + p3_p2_receiver: p3_p2.1, + } + } + + pub fn get_party_networks(self) -> [PartyTestNetwork; 3] { + let party1 = PartyTestNetwork { + id: PartyID::ID0, + send_prev: self.p1_p3_sender, + recv_prev: self.p3_p1_receiver, + send_next: self.p1_p2_sender, + recv_next: self.p2_p1_receiver, + stats: [0; 4], + }; + + let party2 = PartyTestNetwork { + id: PartyID::ID1, + send_prev: self.p2_p1_sender, + recv_prev: self.p1_p2_receiver, + send_next: self.p2_p3_sender, + recv_next: self.p3_p2_receiver, + stats: [0; 4], + }; + + let party3 = PartyTestNetwork { + id: PartyID::ID2, + send_prev: self.p3_p2_sender, + recv_prev: self.p2_p3_receiver, + send_next: self.p3_p1_sender, + recv_next: self.p1_p3_receiver, + stats: [0; 4], + }; + + [party1, party2, party3] + } +} + +pub struct TestNetworkEstablisher { + id: PartyID, + test_network: VecDeque, +} + +pub struct PartyTestNetwork { + id: PartyID, + send_prev: UnboundedSender, + send_next: UnboundedSender, + recv_prev: UnboundedReceiver, + recv_next: UnboundedReceiver, + stats: [usize; 4], // [sent_prev, sent_next, recv_prev, recv_next] +} + +impl From> for TestNetworkEstablisher { + fn from(net: VecDeque) -> Self { + Self { + id: net.front().unwrap().id, + test_network: net, + } + } +} + +impl PartyTestNetwork { + pub const NUM_PARTIES: usize = 3; +} + +impl NetworkEstablisher for TestNetworkEstablisher { + fn get_id(&self) -> PartyID { + self.id + } + + fn get_num_parties(&self) -> usize { + 3 + } + + async fn open_channel(&mut self) -> Result { + self.test_network + .pop_front() + .ok_or(eyre!("test config error".to_owned())) + } + + async fn shutdown(self) -> Result<(), Error> { + Ok(()) + } + + fn get_send_receive(&self, _: usize) -> std::io::Result<(u64, u64)> { + unreachable!() + } + + fn print_connection_stats(&self, _: &mut impl std::io::Write) -> std::io::Result<()> { + unreachable!() + } +} + +impl NetworkTrait for PartyTestNetwork { + async fn shutdown(self) -> Result<(), IOError> { + Ok(()) + } + + async fn send(&mut self, id: PartyID, data: Bytes) -> std::io::Result<()> { + tracing::trace!("send_id {}->{}: {:?}", self.id, id, data); + let res = if id == self.id.next_id() { + self.stats[1] += data.len(); + self.send_next + .send(data) + .map_err(|_| IOError::new(IOErrorKind::Other, "Send failed")) + } else if id == self.id.prev_id() { + self.stats[0] += data.len(); + self.send_prev + .send(data) + .map_err(|_| IOError::new(IOErrorKind::Other, "Send failed")) + } else { + Err(IOError::new(io::ErrorKind::Other, "Invalid ID")) + }; + + tracing::trace!("send_id {}->{}: done", self.id, id); + res + } + + async fn receive(&mut self, id: PartyID) -> std::io::Result { + tracing::trace!("recv_id {}<-{}: ", self.id, id); + let buf = if id == self.id.prev_id() { + let data = self + .recv_prev + .recv() + .await + .ok_or_else(|| IOError::new(IOErrorKind::Other, "Receive failed"))?; + self.stats[2] += data.len(); + data + } else if id == self.id.next_id() { + let data = self + .recv_next + .recv() + .await + .ok_or_else(|| IOError::new(IOErrorKind::Other, "Receive failed"))?; + self.stats[3] += data.len(); + data + } else { + return Err(io::Error::new(io::ErrorKind::Other, "Invalid ID")); + }; + tracing::trace!("recv_id {}<-{}: done", self.id, id); + + Ok(BytesMut::from(buf.as_ref())) + } + + async fn broadcast(&mut self, data: Bytes) -> Result, io::Error> { + let mut result = Vec::with_capacity(3); + for id in 0..3 { + if id != usize::from(self.id) { + self.send(PartyID::try_from(id).unwrap(), data.clone()) + .await?; + } + } + for id in 0..3 { + if id == usize::from(self.id) { + result.push(BytesMut::from(data.as_ref())); + } else { + result.push(self.receive(PartyID::try_from(id).unwrap()).await?); + } + } + Ok(result) + } + + fn get_id(&self) -> PartyID { + self.id + } + + async fn send_next_id(&mut self, data: Bytes) -> Result<(), IOError> { + tracing::trace!("send {}->{}: {:?}", self.id, self.id.next_id(), data); + self.stats[1] += data.len(); + let res = self + .send_next + .send(data) + .map_err(|_| IOError::new(IOErrorKind::Other, "Send failed")); + tracing::trace!("send {}->{}: done", self.id, self.id.next_id()); + res + } + + async fn send_prev_id(&mut self, data: Bytes) -> Result<(), IOError> { + tracing::trace!("send {}->{}: {:?}", self.id, self.id.prev_id(), data); + self.stats[0] += data.len(); + let res = self + .send_prev + .send(data) + .map_err(|_| IOError::new(IOErrorKind::Other, "Send failed")); + tracing::trace!("send {}->{}: done", self.id, self.id.prev_id()); + res + } + + async fn receive_prev_id(&mut self) -> Result { + tracing::trace!("recv {}<-{}: ", self.id, self.id.prev_id()); + let buf = self + .recv_prev + .recv() + .await + .ok_or_else(|| IOError::new(IOErrorKind::Other, "Receive failed"))?; + self.stats[2] += buf.len(); + + tracing::trace!("recv {}<-{}: done", self.id, self.id.prev_id()); + Ok(BytesMut::from(buf.as_ref())) + } + + async fn receive_next_id(&mut self) -> Result { + tracing::trace!("recv {}<-{}: ", self.id, self.id.next_id()); + let buf = self + .recv_next + .recv() + .await + .ok_or_else(|| IOError::new(IOErrorKind::Other, "Receive failed"))?; + self.stats[3] += buf.len(); + + tracing::trace!("recv {}<-{}: done", self.id, self.id.next_id()); + Ok(BytesMut::from(buf.as_ref())) + } + + fn blocking_send( + &mut self, + id: PartyID, + data: Bytes, + ) -> Result<(), super::network_trait::IoError> { + tracing::trace!("send_id {}->{}: {:?}", self.id, id, data); + let res = if id == self.id.next_id() { + self.blocking_send_next_id(data) + } else { + self.blocking_send_prev_id(data) + }; + tracing::trace!("send_id {}->{}: done", self.id, id); + res + } + + fn blocking_send_next_id(&mut self, data: Bytes) -> Result<(), super::network_trait::IoError> { + tracing::trace!("send {}->{}: {:?}", self.id, self.id.next_id(), data); + self.stats[1] += data.len(); + let res = self + .send_next + .send(data) + .map_err(|_| IOError::new(IOErrorKind::Other, "Send failed")); + tracing::trace!("send {}->{}: done", self.id, self.id.next_id()); + res + } + + fn blocking_send_prev_id(&mut self, data: Bytes) -> Result<(), super::network_trait::IoError> { + tracing::trace!("send {}->{}: {:?}", self.id, self.id.prev_id(), data); + self.stats[0] += data.len(); + let res = self + .send_prev + .send(data) + .map_err(|_| IOError::new(IOErrorKind::Other, "Send failed")); + tracing::trace!("send {}->{}: done", self.id, self.id.prev_id()); + res + } + + fn blocking_receive(&mut self, id: PartyID) -> Result { + tracing::trace!("recv_id {}<-{}: ", self.id, id); + let buf = if id == self.id.next_id() { + self.blocking_receive_next_id() + } else { + self.blocking_receive_prev_id() + }; + tracing::trace!("recv_id {}<-{}: done", self.id, id); + buf + } + + fn blocking_receive_prev_id(&mut self) -> Result { + tracing::trace!("recv {}<-{}: ", self.id, self.id.prev_id()); + let buf = self + .recv_prev + .blocking_recv() + .ok_or_else(|| IOError::new(IOErrorKind::Other, "Receive failed"))?; + self.stats[2] += buf.len(); + + tracing::trace!("recv {}<-{}: done", self.id, self.id.prev_id()); + Ok(BytesMut::from(buf.as_ref())) + } + + fn blocking_receive_next_id(&mut self) -> Result { + tracing::trace!("recv {}<-{}: ", self.id, self.id.next_id()); + let buf = self + .recv_next + .blocking_recv() + .ok_or_else(|| IOError::new(IOErrorKind::Other, "Receive failed"))?; + self.stats[3] += buf.len(); + + tracing::trace!("recv {}<-{}: done", self.id, self.id.next_id()); + Ok(BytesMut::from(buf.as_ref())) + } + + fn blocking_broadcast( + &mut self, + data: Bytes, + ) -> Result, super::network_trait::IoError> { + let mut result = Vec::with_capacity(3); + for id in 0..3 { + if id != usize::from(self.id) { + self.blocking_send(PartyID::try_from(id).unwrap(), data.clone())? + } + } + for id in 0..3 { + if id == usize::from(self.id) { + result.push(BytesMut::from(data.as_ref())); + } else { + result.push(self.blocking_receive(PartyID::try_from(id).unwrap())?); + } + } + Ok(result) + } +} diff --git a/iris-mpc-cpu/src/prelude.rs b/iris-mpc-cpu/src/prelude.rs new file mode 100644 index 000000000..5fe3e32c8 --- /dev/null +++ b/iris-mpc-cpu/src/prelude.rs @@ -0,0 +1,7 @@ +pub use super::{ + networks::{ + network_trait::{NetworkEstablisher, NetworkTrait}, + test_network::{PartyTestNetwork, TestNetwork3p, TestNetworkEstablisher}, + }, + protocol::iris_worker::IrisWorker, +}; diff --git a/iris-mpc-cpu/src/protocol/binary.rs b/iris-mpc-cpu/src/protocol/binary.rs new file mode 100644 index 000000000..a0d04588f --- /dev/null +++ b/iris-mpc-cpu/src/protocol/binary.rs @@ -0,0 +1,671 @@ +use super::iris_worker::IrisWorker; +use crate::{ + networks::network_trait::NetworkTrait, + protocol::iris_worker::{A, A_BITS, B_BITS}, + shares::{ + bit::Bit, + int_ring::IntRing2k, + ring_impl::RingElement, + share::Share, + vecshare::{SliceShare, VecShare}, + }, + utils::Utils, +}; +use eyre::{eyre, Error}; +use iris_mpc_common::id::PartyID; +use num_traits::{One, Zero}; +use rand::{distributions::Standard, prelude::Distribution, Rng}; +use std::ops::SubAssign; + +impl IrisWorker { + pub(crate) fn mul_lift_2k(val: &Share) -> Share + where + u32: From, + { + let a = (u32::from(val.a.0)) << K; + let b = (u32::from(val.b.0)) << K; + Share::new(RingElement(a), RingElement(b)) + } + + pub(crate) fn mul_lift_2k_many(vals: SliceShare) -> VecShare + where + u32: From, + { + VecShare::new_vec( + vals.iter() + .map(|val| Self::mul_lift_2k::(val)) + .collect(), + ) + } + + pub(crate) fn a2b_pre(&self, x: Share) -> (Share, Share, Share) { + let (a, b) = x.get_ab(); + + let mut x1 = Share::zero(); + let mut x2 = Share::zero(); + let mut x3 = Share::zero(); + + match self.network.get_id() { + PartyID::ID0 => { + x1.a = a; + x3.b = b; + } + PartyID::ID1 => { + x2.a = a; + x1.b = b; + } + PartyID::ID2 => { + x3.a = a; + x2.b = b; + } + } + (x1, x2, x3) + } + + pub(crate) fn and_many_send( + &mut self, + a: SliceShare<'_, T>, + b: SliceShare<'_, T>, + ) -> Result>, Error> + where + Standard: Distribution, + { + if a.len() != b.len() { + return Err(eyre!("InvalidSize in and_many_send")); + } + + let mut shares_a = Vec::with_capacity(a.len()); + for (a_, b_) in a.iter().zip(b.iter()) { + let rand = self.prf.gen_binary_zero_share::(); + let mut c = a_ & b_; + c ^= rand; + shares_a.push(c); + } + + self.network + .blocking_send_next_id(Utils::ring_slice_to_bytes(&shares_a))?; + Ok(shares_a) + } + + pub(crate) fn and_many_receive( + &mut self, + shares_a: Vec>, + ) -> Result, Error> + where + Standard: Distribution, + { + let len = shares_a.len(); + let response = self.network.blocking_receive_prev_id()?; + let shares_b = Utils::ring_iter_from_bytes(response, len)?; + + let res = VecShare::from_avec_biter(shares_a, shares_b); + Ok(res) + } + + pub(crate) fn and_many( + &mut self, + a: SliceShare<'_, T>, + b: SliceShare<'_, T>, + ) -> Result, Error> + where + Standard: Distribution, + { + let shares_a = self.and_many_send(a, b)?; + self.and_many_receive(shares_a) + } + + pub(crate) fn transposed_pack_and_send( + &mut self, + x1: Vec>, + x2: Vec>, + ) -> Result>>, Error> + where + Standard: Distribution, + { + let len = x1.len(); + debug_assert_eq!(len, x2.len()); + + let mut send = Vec::with_capacity(len); + for (x1, x2) in x1.iter().zip(x2.iter()) { + let send_ = self.and_many_send(x1.as_slice(), x2.as_slice())?; + send.push(send_); + } + Ok(send) + } + + pub(crate) fn transposed_pack_and_receive( + &mut self, + shares_a: Vec>>, + ) -> Result>, Error> + where + Standard: Distribution, + { + let mut x3 = Vec::with_capacity(shares_a.len()); + + for shares_a_ in shares_a { + let res = self.and_many_receive(shares_a_)?; + x3.push(res); + } + + Ok(x3) + } + + pub(crate) fn transposed_pack_and( + &mut self, + x1: Vec>, + x2: Vec>, + ) -> Result>, Error> + where + Standard: Distribution, + { + let send = self.transposed_pack_and_send(x1, x2)?; + self.transposed_pack_and_receive(send) + } + + pub(crate) fn transposed_pack_xor( + x1: &[VecShare], + x2: &[VecShare], + ) -> Vec> { + let len = x1.len(); + debug_assert_eq!(len, x2.len()); + + let mut res = Vec::with_capacity(len); + for (x1, x2) in x1.iter().zip(x2.iter()) { + res.push(x1.as_slice() ^ x2.as_slice()); + } + res + } + + pub(crate) fn transposed_pack_xor_assign( + x1: &mut [VecShare], + x2: &[VecShare], + ) { + let len = x1.len(); + debug_assert_eq!(len, x2.len()); + + for (x1, x2) in x1.iter_mut().zip(x2.iter()) { + *x1 ^= x2.as_slice(); + } + } + + fn bit_inject_ot_2round_sender( + &mut self, + input: VecShare, + ) -> Result, Error> + where + Standard: Distribution, + { + let len = input.len(); + let mut m0 = Vec::with_capacity(len); + let mut m1 = Vec::with_capacity(len); + let mut shares = VecShare::with_capacity(len); + + for inp in input.into_iter() { + let (a, b) = inp.get_ab(); + // new shares + let (c3, c2) = self.prf.gen_rands::>(); + // mask of the ot + let w0 = self.prf.get_my_prf().gen::>(); + let w1 = self.prf.get_my_prf().gen::>(); + + shares.push(Share::new(c3, c2)); + let c = c3 + c2; + let xor = RingElement(T::from((a ^ b).convert().convert())); + let m0_ = xor - c; + let m1_ = (xor ^ RingElement::one()) - c; + m0.push(m0_ ^ w0); + m1.push(m1_ ^ w1); + } + self.network + .blocking_send_prev_id(Utils::ring_slice_to_bytes(&m0))?; + self.network + .blocking_send_prev_id(Utils::ring_slice_to_bytes(&m1))?; + + Ok(shares) + } + + fn bit_inject_ot_2round_receiver( + &mut self, + input: VecShare, + ) -> Result, Error> + where + Standard: Distribution, + { + let len = input.len(); + let m0_bytes = self.network.blocking_receive_next_id()?; + let m1_bytes = self.network.blocking_receive_next_id()?; + let wc_bytes = self.network.blocking_receive_prev_id()?; + + let m0 = Utils::ring_iter_from_bytes(m0_bytes, len)?; + let m1 = Utils::ring_iter_from_bytes(m1_bytes, len)?; + + let wc = Utils::ring_iter_from_bytes(wc_bytes, len)?; + + let mut shares = VecShare::with_capacity(len); + let mut send = Vec::with_capacity(len); + + for ((inp, wc), (m0, m1)) in input.into_iter().zip(wc).zip(m0.zip(m1)) { + // new share + let c2 = self.prf.get_my_prf().gen::>(); + + let choice = inp.get_b().convert().convert(); + let xor = if choice { wc ^ m1 } else { wc ^ m0 }; + + send.push(xor); + shares.push(Share::new(c2, xor)); + } + + // Reshare to Helper + self.network + .blocking_send_prev_id(Utils::ring_slice_to_bytes(&send))?; + + Ok(shares) + } + + fn bit_inject_ot_2round_helper( + &mut self, + input: VecShare, + ) -> Result, Error> + where + Standard: Distribution, + { + let len = input.len(); + let mut wc = Vec::with_capacity(len); + let mut shares = VecShare::with_capacity(len); + + for inp in input.into_iter() { + // new share + let c3 = self.prf.get_prev_prf().gen::>(); + shares.push(Share::new(RingElement::zero(), c3)); + + // mask of the ot + let w0 = self.prf.get_prev_prf().gen::>(); + let w1 = self.prf.get_prev_prf().gen::>(); + + let choice = inp.get_a().convert().convert(); + if choice { + wc.push(w1); + } else { + wc.push(w0); + } + } + self.network + .blocking_send_next_id(Utils::ring_slice_to_bytes(&wc))?; + + // Receive Reshare + let c1_bytes = self.network.blocking_receive_next_id()?; + let c1 = Utils::ring_iter_from_bytes(c1_bytes, len)?; + + for (s, c1) in shares.iter_mut().zip(c1) { + s.a = c1; + } + Ok(shares) + } + + // TODO this is inbalanced, so a real implementation should actually rotate + // parties around + pub(crate) fn bit_inject_ot_2round( + &mut self, + input: VecShare, + ) -> Result, Error> + where + Standard: Distribution, + { + let res = match self.get_party_id() { + PartyID::ID0 => { + // OT Helper + self.bit_inject_ot_2round_helper(input)? + } + PartyID::ID1 => { + // OT Receiver + self.bit_inject_ot_2round_receiver(input)? + } + PartyID::ID2 => { + // OT Sender + self.bit_inject_ot_2round_sender(input)? + } + }; + Ok(res) + } + + // TODO a dedicated bitextraction for just one element would be more + // efficient + pub fn single_extract_msb_u16( + &mut self, + x: Share, + ) -> Result, Error> { + let (a, b) = self + .extract_msb_u16::<{ u16::BITS as usize }>(VecShare::new_vec(vec![x]))? + .get_at(0) + .get_ab(); + + Ok(Share::new(a.get_bit_as_bit(0), b.get_bit_as_bit(0))) + } + + // TODO a dedicated bitextraction for just one element would be more + // efficient + pub fn single_extract_msb_u32( + &mut self, + x: Share, + ) -> Result, Error> { + let (a, b) = self + .extract_msb_u32::<{ u16::BITS as usize }>(VecShare::new_vec(vec![x]))? + .get_at(0) + .get_ab(); + + Ok(Share::new(a.get_bit_as_bit(0), b.get_bit_as_bit(0))) + } + + pub fn extract_msb_u16( + &mut self, + x_: VecShare, + ) -> Result, Error> { + // let truncate_len = x_.len(); + let x = x_.transpose_pack_u64_with_len::(); + self.extract_msb::(x) + } + + pub fn extract_msb_u32( + &mut self, + x_: VecShare, + ) -> Result, Error> { + // let truncate_len = x_.len(); + let x = x_.transpose_pack_u64_with_len::(); + self.extract_msb::(x) + } + + // Extracts bit at position K + fn extract_msb( + &mut self, + x: Vec>, + ) -> Result, Error> { + let len = x.len(); + + let mut x1 = Vec::with_capacity(len); + let mut x2 = Vec::with_capacity(len); + let mut x3 = Vec::with_capacity(len); + + for x_ in x.into_iter() { + let len_ = x_.len(); + let mut x1_ = VecShare::with_capacity(len_); + let mut x2_ = VecShare::with_capacity(len_); + let mut x3_ = VecShare::with_capacity(len_); + for x__ in x_.into_iter() { + let (x1__, x2__, x3__) = self.a2b_pre(x__); + x1_.push(x1__); + x2_.push(x2__); + x3_.push(x3__); + } + x1.push(x1_); + x2.push(x2_); + x3.push(x3_); + } + + self.binary_add_3_get_msb(x1, x2, x3) + } + + fn binary_add_3_get_two_carries( + &mut self, + x1: Vec>, + x2: Vec>, + x3: Vec>, + truncate_len: usize, + ) -> Result<(VecShare, VecShare), Error> + where + Standard: Distribution, + { + let len = x1.len(); + debug_assert!(len == x2.len() && len == x3.len()); + + // Full adder to get 2 * c and s + let mut x2x3 = x2; + Self::transposed_pack_xor_assign(&mut x2x3, &x3); + let s = Self::transposed_pack_xor(&x1, &x2x3); + let mut x1x3 = x1; + Self::transposed_pack_xor_assign(&mut x1x3, &x3); + let mut c = self.transposed_pack_and(x1x3, x2x3)?; + Self::transposed_pack_xor_assign(&mut c, &x3); + + // Add 2c + s via a ripple carry adder + // LSB of c is 0 + // First round: half adder can be skipped due to LSB of c being 0 + let mut a = s; + let mut b = c; + + // First full adder (carry is 0) + let mut c = self.and_many(a[1].as_slice(), b[0].as_slice())?; + + // For last round + let mut b_msb = b.pop().expect("Enough elements present"); + + // 2 -> k + for (a_, b_) in a.iter_mut().skip(2).zip(b.iter_mut().skip(1)) { + *a_ ^= c.as_slice(); + *b_ ^= c.as_slice(); + let tmp_c = self.and_many(a_.as_slice(), b_.as_slice())?; + c ^= tmp_c; + } + + // Finally, last bit of a is 0 + let res2 = self.and_many(b_msb.as_slice(), c.as_slice())?; + b_msb ^= c; + + // Extract bits for outputs + let mut res1 = b_msb.convert_to_bits(); + res1.truncate(truncate_len); + let mut res2 = res2.convert_to_bits(); + res2.truncate(truncate_len); + + Ok((res1, res2)) + } + + pub(crate) fn binary_add_3_get_msb( + &mut self, + x1: Vec>, + x2: Vec>, + mut x3: Vec>, + // truncate_len: usize, + ) -> Result, Error> + where + Standard: Distribution, + { + let len = x1.len(); + debug_assert!(len == x2.len() && len == x3.len()); + + // Full adder to get 2 * c and s + let mut x2x3 = x2; + Self::transposed_pack_xor_assign(&mut x2x3, &x3); + let s = Self::transposed_pack_xor(&x1, &x2x3); + let mut x1x3 = x1; + Self::transposed_pack_xor_assign(&mut x1x3, &x3); + // 2 * c + x1x3.pop().expect("Enough elements present"); + x2x3.pop().expect("Enough elements present"); + x3.pop().expect("Enough elements present"); + let mut c = self.transposed_pack_and(x1x3, x2x3)?; + Self::transposed_pack_xor_assign(&mut c, &x3); + + // Add 2c + s via a ripple carry adder + // LSB of c is 0 + // First round: half adder can be skipped due to LSB of c being 0 + let mut a = s; + let mut b = c; + + // First full adder (carry is 0) + let mut c = self.and_many(a[1].as_slice(), b[0].as_slice())?; + + // For last round + let mut a_msb = a.pop().expect("Enough elements present"); + let b_msb = b.pop().expect("Enough elements present"); + + // 2 -> k-1 + for (a_, b_) in a.iter_mut().skip(2).zip(b.iter_mut().skip(1)) { + *a_ ^= c.as_slice(); + *b_ ^= c.as_slice(); + let tmp_c = self.and_many(a_.as_slice(), b_.as_slice())?; + c ^= tmp_c; + } + + a_msb ^= b_msb; + a_msb ^= c; + + // Extract bits for outputs + let res = a_msb; + // let mut res = a_msb.convert_to_bits(); + // res.truncate(truncate_len); + + Ok(res) + } + + pub(crate) fn transposed_padded_len(len: usize) -> usize { + let padded_len = (len + 63) / 64; + padded_len * 64 + } + + pub(crate) fn lift( + &mut self, + shares: VecShare, + ) -> Result, Error> { + let len = shares.len(); + let padded_len = Self::transposed_padded_len(len); + + let mut x_a = VecShare::with_capacity(padded_len); + for share in shares.iter() { + x_a.push(Share::new( + RingElement(share.a.0 as u32), + RingElement(share.b.0 as u32), + )); + } + + let x = shares.transpose_pack_u64(); + + let len_ = x.len(); + let mut x1 = Vec::with_capacity(len_); + let mut x2 = Vec::with_capacity(len_); + let mut x3 = Vec::with_capacity(len_); + + for x_ in x.into_iter() { + let len__ = x_.len(); + let mut x1_ = VecShare::with_capacity(len__); + let mut x2_ = VecShare::with_capacity(len__); + let mut x3_ = VecShare::with_capacity(len__); + for x__ in x_.into_iter() { + let (x1__, x2__, x3__) = self.a2b_pre(x__); + x1_.push(x1__); + x2_.push(x2__); + x3_.push(x3__); + } + x1.push(x1_); + x2.push(x2_); + x3.push(x3_); + } + + let (mut b1, b2) = self.binary_add_3_get_two_carries(x1, x2, x3, len)?; + + b1.extend(b2); + + // We need bit1 * 2^{ShareRing::K+1} mod 2^{ShareRing::K+K} and bit2 * + // 2^{ShareRing::K+1} mod 2^{ShareRing::K+K} So we inject bit to mod + // 2^{K-1} and mod 2^{K-2} and use the mul_lift_2k function TODO: This + // one is not optimized: We send too much, since we need less than K + // bits + debug_assert!(K <= 16); // otherwise u16 does not work + let mut b = self.bit_inject_ot_2round::(b1)?; + let (b1, b2) = b.split_at_mut(len); + + // Make the result mod 2^{K-1} and mod 2^{K-2} (Not required since we bitextract + // the correct one later) Self::share_bit_mod(&mut b1, K as u32); + // Self::share_bit_mod(&mut b2, K as u32 - 1); + + let b1 = Self::mul_lift_2k_many::<_, { u16::K as u64 }>(b1.to_slice()); + let b2 = Self::mul_lift_2k_many::<_, { u16::K as u64 + 1 }>(b2.to_slice()); + + // Finally, compute the result + x_a.sub_assign(b1); + x_a.sub_assign(b2); + Ok(x_a) + } + + // Compute code_dots > a/b * mask_dots + // via MSB(a * mask_dots - b * code_dots) + pub fn compare_threshold_masked( + &mut self, + code_dot: Share, + mask_dot: Share, + ) -> Result, Error> { + debug_assert!(A_BITS as u64 <= B_BITS); + + let y = Self::mul_lift_2k::<_, B_BITS>(&code_dot); + // TODO a dedicated bitextraction for just one element would be more + // efficient + let mut x = self.lift::<{ B_BITS as usize }>(VecShare::new_vec(vec![mask_dot]))?; + debug_assert_eq!(x.len(), 1); + let mut x = x.pop().expect("Enough elements present"); + x *= A as u32; + x -= y; + + self.single_extract_msb_u32::<{ u16::K + B_BITS as usize }>(x) + } + + // Compute code_dots > a/b * mask_dots + // via MSB(a * mask_dots - b * code_dots) + pub fn compare_threshold_masked_many( + &mut self, + code_dots: VecShare, + mask_dots: VecShare, + ) -> Result, Error> { + debug_assert!(A_BITS as u64 <= B_BITS); + let len = code_dots.len(); + assert_eq!(len, mask_dots.len()); + + let y = Self::mul_lift_2k_many::<_, B_BITS>(code_dots.as_slice()); + let mut x = self.lift::<{ B_BITS as usize }>(mask_dots)?; + for x_ in x.iter_mut() { + *x_ *= A as u32; + } + + x.sub_assign(y); + self.extract_msb_u32::<{ u16::K + B_BITS as usize }>(x) + } + + pub fn open_bit(&mut self, share: Share) -> Result { + let c = Utils::blocking_send_and_receive_value(&mut self.network, &share.b)?; + Ok((share.a ^ share.b ^ c).convert().convert()) + } + + pub fn open_bit_many(&mut self, shares: VecShare) -> Result, Error> { + let shares_b = shares.iter().map(|s| &s.b); + let bytes = Utils::blocking_send_iter_and_receive(&mut self.network, shares_b)?; + let shares_c = Utils::ring_iter_from_bytes(bytes, shares.len())?; + let res = shares + .into_iter() + .zip(shares_c) + .map(|(s, c)| { + let (a, b) = s.get_ab(); + (a ^ b ^ c).convert().convert() + }) + .collect(); + Ok(res) + } + + pub fn open_bin(&mut self, share: Share) -> Result { + let c = Utils::blocking_send_and_receive_value(&mut self.network, &share.b)?; + Ok((share.a ^ share.b ^ c).convert()) + } + + pub fn open_bin_many(&mut self, shares: VecShare) -> Result, Error> { + let shares_b = shares.iter().map(|s| &s.b); + let bytes: bytes::BytesMut = + Utils::blocking_send_iter_and_receive(&mut self.network, shares_b)?; + let shares_c = Utils::ring_iter_from_bytes(bytes, shares.len())?; + let res = shares + .into_iter() + .zip(shares_c) + .map(|(s, c)| { + let (a, b) = s.get_ab(); + (a ^ b ^ c).convert() + }) + .collect(); + Ok(res) + } +} diff --git a/iris-mpc-cpu/src/protocol/dots.rs b/iris-mpc-cpu/src/protocol/dots.rs new file mode 100644 index 000000000..995daa996 --- /dev/null +++ b/iris-mpc-cpu/src/protocol/dots.rs @@ -0,0 +1,125 @@ +use super::iris_worker::IrisWorker; +use crate::{ + networks::network_trait::NetworkTrait, + shares::{ + ring_impl::RingElement, + share::Share, + vecshare::{SliceShare, VecShare}, + }, + utils::Utils, +}; +use eyre::{eyre, Error}; +use iris_mpc_common::{ + galois_engine::degree4::GaloisRingIrisCodeShare, iris_db::iris::IrisCodeArray, +}; + +impl IrisWorker { + pub async fn async_rep3_dot( + &mut self, + a: SliceShare<'_, u16>, + b: SliceShare<'_, u16>, + ) -> Result, Error> { + if a.len() != IrisCodeArray::IRIS_CODE_SIZE || b.len() != IrisCodeArray::IRIS_CODE_SIZE { + return Err(eyre!("Error::InvalidSize")); + } + + let mut rand = self.prf.gen_zero_share(); + for (a_, b_) in a.iter().zip(b.iter()) { + rand += a_ * b_; + } + + // Network: reshare + let res_a = rand; + let bytes_to_send = Utils::ring_to_bytes(&res_a); + self.network.send_next_id(bytes_to_send).await?; + let response = self.network.receive_prev_id().await?; + let res_b = Utils::ring_from_bytes(response)?; + + let res = Share::new(res_a, res_b); + Ok(res) + } + + pub fn rep3_dot( + &mut self, + a: SliceShare<'_, u16>, + b: SliceShare<'_, u16>, + ) -> Result, Error> { + if a.len() != IrisCodeArray::IRIS_CODE_SIZE || b.len() != IrisCodeArray::IRIS_CODE_SIZE { + return Err(eyre!("Error::InvalidSize")); + } + + let mut rand = self.prf.gen_zero_share(); + for (a_, b_) in a.iter().zip(b.iter()) { + rand += a_ * b_; + } + + // Network: reshare + let res_a = rand; + let bytes_to_send = Utils::ring_to_bytes(&res_a); + let response = Utils::blocking_send_and_receive(&mut self.network, bytes_to_send)?; + let res_b = Utils::ring_from_bytes(response)?; + + let res = Share::new(res_a, res_b); + Ok(res) + } + + pub fn rep3_dot_many( + &mut self, + a: SliceShare<'_, u16>, + b: &[VecShare], + ) -> Result, Error> { + let len = b.len(); + if a.len() != IrisCodeArray::IRIS_CODE_SIZE { + return Err(eyre!("Error::InvalidSize")); + } + + let mut shares_a = Vec::with_capacity(len); + + for b_ in b.iter() { + let mut rand = self.prf.gen_zero_share(); + if a.len() != b_.len() { + return Err(eyre!("Error::InvalidSize")); + } + for (a__, b__) in a.iter().zip(b_.iter()) { + rand += a__ * b__; + } + shares_a.push(rand); + } + + // Network: reshare + let bytes = Utils::blocking_send_slice_and_receive(&mut self.network, &shares_a)?; + let shares_b = Utils::ring_iter_from_bytes(bytes, len)?; + let res = VecShare::from_avec_biter(shares_a, shares_b); + + Ok(res) + } + + pub fn shamir_dot( + &mut self, + a: &GaloisRingIrisCodeShare, + b: &GaloisRingIrisCodeShare, + ) -> Result, Error> { + let mut rand = self.prf.gen_zero_share(); + let res = RingElement(a.trick_dot(b)); + rand += res; + Ok(rand) + } + + pub fn shamir_dot_many( + &mut self, + a: &GaloisRingIrisCodeShare, + b: &[GaloisRingIrisCodeShare], + ) -> Result>, Error> { + let len = b.len(); + let mut shares = Vec::with_capacity(len); + + for b_ in b.iter() { + let mut rand = self.prf.gen_zero_share(); + let res = RingElement(a.trick_dot(b_)); + rand += res; + shares.push(rand); + } + + Ok(shares) + } +} diff --git a/iris-mpc-cpu/src/protocol/iris_worker.rs b/iris-mpc-cpu/src/protocol/iris_worker.rs new file mode 100644 index 000000000..641c08d57 --- /dev/null +++ b/iris-mpc-cpu/src/protocol/iris_worker.rs @@ -0,0 +1,360 @@ +use super::prf::{Prf, PrfSeed}; +use crate::{ + networks::network_trait::NetworkTrait, + shares::{ + bit::Bit, + int_ring::IntRing2k, + ring_impl::RingElement, + share::Share, + vecshare::{SliceShare, VecShare}, + }, + utils::Utils, +}; +use bytes::{Buf, Bytes, BytesMut}; +use eyre::{eyre, Error, Result}; +use iris_mpc_common::{ + galois_engine::degree4::GaloisRingIrisCodeShare, id::PartyID, iris_db::iris::IrisCodeArray, +}; +use static_assertions::const_assert; + +pub(crate) const MATCH_THRESHOLD_RATIO: f64 = iris_mpc_common::iris_db::iris::MATCH_THRESHOLD_RATIO; +pub(crate) const B_BITS: u64 = 16; +pub(crate) const B: u64 = 1 << B_BITS; +pub(crate) const A: u64 = ((1. - 2. * MATCH_THRESHOLD_RATIO) * B as f64) as u64; +pub(crate) const A_BITS: u32 = u64::BITS - A.leading_zeros(); + +pub struct IrisWorker { + pub(crate) network: N, + pub(crate) prf: Prf, +} + +impl IrisWorker { + pub fn new(network: N) -> Self { + const_assert!(MATCH_THRESHOLD_RATIO > 0.0 && MATCH_THRESHOLD_RATIO < 1.0); + // checking that code size fits on 15 bits. + const_assert!( + u16::MAX as usize >= iris_mpc_common::iris_db::iris::IrisCodeArray::IRIS_CODE_SIZE + ); + Self { + network, + prf: Prf::default(), + } + } + + pub fn get_party_id(&self) -> PartyID { + self.network.get_id() + } + + pub(crate) fn bytes_to_seed(mut bytes: BytesMut) -> Result { + if bytes.len() != std::mem::size_of::() { + Err(eyre!("InvalidMessageSize")) + } else { + let mut their_seed: PrfSeed = PrfSeed::default(); + bytes.copy_to_slice(&mut their_seed); + Ok(their_seed) + } + } + + pub async fn setup_prf(&mut self) -> Result<(), Error> { + let seed = Prf::gen_seed(); + let data = Bytes::from_iter(seed.into_iter()); + self.network.send_next_id(data).await?; + let response = self.network.receive_prev_id().await?; + let their_seed = Self::bytes_to_seed(response)?; + self.prf = Prf::new(seed, their_seed); + Ok(()) + } + + pub(crate) fn rep3_get_cmp_diff(&self, dot: &mut Share, mask_ones: usize) { + let threshold: u16 = ((mask_ones as f64 * (1. - 2. * MATCH_THRESHOLD_RATIO)) as usize) + .try_into() + .expect("Sizes are checked in constructor"); + *dot = dot.sub_from_const(threshold, self.network.get_id()); + } + + pub(crate) fn shamir_get_cmp_diff(&self, dot: &mut RingElement, mask_ones: usize) { + let threshold: u16 = ((mask_ones as f64 * (1. - 2. * MATCH_THRESHOLD_RATIO)) as usize) + .try_into() + .expect("Sizes are checked in constructor"); + *dot = RingElement(threshold) - *dot; + } + + pub fn combine_masks(mask_a: &IrisCodeArray, mask_b: &IrisCodeArray) -> IrisCodeArray { + *mask_a & *mask_b + } + + pub fn shamir_to_rep3_many( + &mut self, + inp: Vec>, + ) -> Result, Error> { + let len = inp.len(); + let shares_a = inp; + let bytes = Utils::blocking_send_slice_and_receive(&mut self.network, &shares_a)?; + let shares_b = Utils::ring_iter_from_bytes(bytes, len)?; + let res = VecShare::from_avec_biter(shares_a, shares_b); + Ok(res) + } + + pub fn shamir_to_rep3(&mut self, inp: RingElement) -> Result, Error> { + let share_a = inp; + let bytes_to_send = Utils::ring_to_bytes(&share_a); + let response = Utils::blocking_send_and_receive(&mut self.network, bytes_to_send)?; + let share_b = Utils::ring_from_bytes(response)?; + + let res = Share::new(share_a, share_b); + Ok(res) + } + + pub fn rep3_compare_iris_public_mask( + &mut self, + a: SliceShare<'_, u16>, + b: SliceShare<'_, u16>, + mask_a: &IrisCodeArray, + mask_b: &IrisCodeArray, + ) -> Result, Error> { + let mask = Self::combine_masks(mask_a, mask_b); + let mask_len = mask.count_ones(); + + let mut dot = self.rep3_dot(a, b)?; + + // a < b <=> msb(a - b) + // Given no overflow, which is enforced in constructor + self.rep3_get_cmp_diff(&mut dot, mask_len); + + self.single_extract_msb_u16::<{ u16::BITS as usize }>(dot) + } + + pub fn rep3_compare_iris_public_mask_many( + &mut self, + a: SliceShare<'_, u16>, + b: &[VecShare], + mask_a: &IrisCodeArray, + mask_b: &[IrisCodeArray], + ) -> Result, Error> { + let amount = b.len(); + if (amount != mask_b.len()) || (amount == 0) { + return Err(eyre!("InvalidSize")); + } + + let masks = mask_b + .iter() + .map(|b| Self::combine_masks(mask_a, b)) + .collect::>(); + let mask_lens: Vec<_> = masks.iter().map(|m| m.count_ones()).collect(); + + let mut dots = self.rep3_dot_many(a, b)?; + + // a < b <=> msb(a - b) + // Given no overflow, which is enforced in constructor + for (dot, mask_len) in dots.iter_mut().zip(mask_lens) { + self.rep3_get_cmp_diff(dot, mask_len); + } + + self.extract_msb_u16::<{ u16::BITS as usize }>(dots) + } + + pub fn rep3_compare_iris_private_mask( + &mut self, + a: SliceShare<'_, u16>, + b: SliceShare<'_, u16>, + mask_a: SliceShare<'_, u16>, + mask_b: SliceShare<'_, u16>, + ) -> Result, Error> { + let amount = b.len(); + if (amount != mask_b.len()) || (amount == 0) { + return Err(eyre!("InvalidSize")); + } + + let code_dots = self.rep3_dot(a, b)?; + let mask_dots = self.rep3_dot(mask_a, mask_b)?; + + // Compute code_dots > a/b * mask_dots + // via MSB(a * mask_dots - b * code_dots) + self.compare_threshold_masked(code_dots, mask_dots) + } + + pub fn rep3_compare_iris_private_mask_many( + &mut self, + a: SliceShare<'_, u16>, + b: &[VecShare], + mask_a: SliceShare<'_, u16>, + mask_b: &[VecShare], + ) -> Result, Error> { + let amount = b.len(); + if (amount != mask_b.len()) || (amount == 0) { + return Err(eyre!("InvalidSize")); + } + + let code_dots = self.rep3_dot_many(a, b)?; + let mask_dots = self.rep3_dot_many(mask_a, mask_b)?; + + // Compute code_dots > a/b * mask_dots + // via MSB(a * mask_dots - b * code_dots) + self.compare_threshold_masked_many(code_dots, mask_dots) + } + + pub fn shamir_compare_iris_public_mask( + &mut self, + a: &mut GaloisRingIrisCodeShare, + b: &GaloisRingIrisCodeShare, + mask_a: &IrisCodeArray, + mask_b: &IrisCodeArray, + ) -> Result, Error> { + // We have to add the lagrange coefficient here + a.preprocess_iris_code_query_share(); + + let mask = Self::combine_masks(mask_a, mask_b); + let mask_len = mask.count_ones(); + + let mut dot = self.shamir_dot(a, b)?; + + // a < b <=> msb(a - b) + // Given no overflow, which is enforced in constructor + self.shamir_get_cmp_diff(&mut dot, mask_len); + + // Network: reshare + let dot = self.shamir_to_rep3(dot)?; + + self.single_extract_msb_u16::<{ u16::BITS as usize }>(dot) + } + + pub fn shamir_compare_iris_public_mask_many( + &mut self, + a: &mut GaloisRingIrisCodeShare, + b: &[GaloisRingIrisCodeShare], + mask_a: &IrisCodeArray, + mask_b: &[IrisCodeArray], + ) -> Result, Error> { + let amount = b.len(); + if (amount != mask_b.len()) || (amount == 0) { + return Err(eyre!("InvalidSize")); + } + + // We have to add the lagrange coefficient here + a.preprocess_iris_code_query_share(); + + let masks = mask_b + .iter() + .map(|b| Self::combine_masks(mask_a, b)) + .collect::>(); + let mask_lens: Vec<_> = masks.iter().map(|m| m.count_ones()).collect(); + + let mut dots = self.shamir_dot_many(a, b)?; + + // a < b <=> msb(a - b) + // Given no overflow, which is enforced in constructor + for (dot, mask_len) in dots.iter_mut().zip(mask_lens) { + self.shamir_get_cmp_diff(dot, mask_len); + } + + // Network: reshare + let dots = self.shamir_to_rep3_many(dots)?; + + self.extract_msb_u16::<{ u16::BITS as usize }>(dots) + } + + pub fn shamir_compare_iris_private_mask( + &mut self, + a: &mut GaloisRingIrisCodeShare, + b: &GaloisRingIrisCodeShare, + mask_a: &mut GaloisRingIrisCodeShare, + mask_b: &GaloisRingIrisCodeShare, + ) -> Result, Error> { + // We have to add the lagrange coefficient here + a.preprocess_iris_code_query_share(); + mask_a.preprocess_iris_code_query_share(); + + let code_dot = self.shamir_dot(a, b)?; + let mask_dot = self.shamir_dot(mask_a, mask_b)?; + + // Network: reshare + let code_dot = self.shamir_to_rep3(code_dot)?; + let mask_dot = self.shamir_to_rep3(mask_dot)?; + + // Compute code_dots > a/b * mask_dots + // via MSB(a * mask_dots - b * code_dots) + self.compare_threshold_masked(code_dot, mask_dot) + } + + pub fn shamir_compare_iris_private_mask_many( + &mut self, + a: &mut GaloisRingIrisCodeShare, + b: &[GaloisRingIrisCodeShare], + mask_a: &mut GaloisRingIrisCodeShare, + mask_b: &[GaloisRingIrisCodeShare], + ) -> Result, Error> { + let amount = b.len(); + if (amount != mask_b.len()) || (amount == 0) { + return Err(eyre!("InvalidSize")); + } + + // We have to add the lagrange coefficient here + a.preprocess_iris_code_query_share(); + mask_a.preprocess_iris_code_query_share(); + + let code_dots = self.shamir_dot_many(a, b)?; + let mask_dots = self.shamir_dot_many(mask_a, mask_b)?; + + // Network: reshare + let code_dots = self.shamir_to_rep3_many(code_dots)?; + let mask_dots = self.shamir_to_rep3_many(mask_dots)?; + + // Compute code_dots > a/b * mask_dots + // via MSB(a * mask_dots - b * code_dots) + self.compare_threshold_masked_many(code_dots, mask_dots) + } + + pub fn open(&mut self, share: Share) -> Result { + let c = Utils::blocking_send_and_receive_value(&mut self.network, &share.b)?; + Ok((share.a + share.b + c).convert()) + } + + pub fn open_t_many(&mut self, shares: VecShare) -> Result, Error> { + let shares_b = shares.iter().map(|s| &s.b); + let bytes = Utils::blocking_send_iter_and_receive(&mut self.network, shares_b)?; + let shares_c = Utils::ring_iter_from_bytes(bytes, shares.len())?; + let res = shares + .into_iter() + .zip(shares_c) + .map(|(s, c)| { + let (a, b) = s.get_ab(); + (a + b + c).convert() + }) + .collect(); + Ok(res) + } + + pub async fn open_async( + &mut self, + share: Share, + ) -> Result, Error> { + let bytes_to_send = Utils::ring_to_bytes(&share.b); + let _ = self.network.send_next_id(bytes_to_send).await; + let response = self.network.receive_prev_id().await?; + let (a, b) = share.get_ab(); + Ok(a + b + Utils::ring_from_bytes(response)?) + } + + pub async fn open_async_t_many( + &mut self, + shares: VecShare, + ) -> Result>, Error> { + let n = shares.len(); + let shares_b: Vec<_> = shares.iter().map(|s| s.b).collect(); + let bytes_to_send = Utils::ring_slice_to_bytes(&shares_b); + + self.network.send_next_id(bytes_to_send).await?; + let response = self.network.receive_prev_id().await?; + let shares_c = Utils::ring_iter_from_bytes(response, n)?; + + let res = shares + .into_iter() + .zip(shares_c) + .map(|(s, c)| { + let (a, b) = s.get_ab(); + a + b + c + }) + .collect(); + Ok(res) + } +} diff --git a/iris-mpc-cpu/src/protocol/mod.rs b/iris-mpc-cpu/src/protocol/mod.rs new file mode 100644 index 000000000..6b884831e --- /dev/null +++ b/iris-mpc-cpu/src/protocol/mod.rs @@ -0,0 +1,4 @@ +pub(crate) mod binary; +pub(crate) mod dots; +pub(crate) mod iris_worker; +pub(crate) mod prf; diff --git a/iris-mpc-cpu/src/protocol/prf.rs b/iris-mpc-cpu/src/protocol/prf.rs new file mode 100644 index 000000000..2f9d1328a --- /dev/null +++ b/iris-mpc-cpu/src/protocol/prf.rs @@ -0,0 +1,66 @@ +use crate::shares::{int_ring::IntRing2k, ring_impl::RingElement}; +use aes_prng::AesRng; +use rand::{distributions::Standard, prelude::Distribution, Rng, SeedableRng}; + +pub type PrfSeed = ::Seed; + +pub struct Prf { + my_prf: AesRng, + prev_prf: AesRng, +} + +impl Default for Prf { + fn default() -> Self { + Self { + my_prf: AesRng::from_entropy(), + prev_prf: AesRng::from_entropy(), + } + } +} + +impl Prf { + pub fn new(my_key: PrfSeed, next_key: PrfSeed) -> Self { + Self { + my_prf: AesRng::from_seed(my_key), + prev_prf: AesRng::from_seed(next_key), + } + } + + pub fn get_my_prf(&mut self) -> &mut AesRng { + &mut self.my_prf + } + + pub fn get_prev_prf(&mut self) -> &mut AesRng { + &mut self.prev_prf + } + + pub fn gen_seed() -> PrfSeed { + let mut rng = AesRng::from_entropy(); + rng.gen::() + } + + pub fn gen_rands(&mut self) -> (T, T) + where + Standard: Distribution, + { + let a = self.my_prf.gen::(); + let b = self.prev_prf.gen::(); + (a, b) + } + + pub fn gen_zero_share(&mut self) -> RingElement + where + Standard: Distribution, + { + let (a, b) = self.gen_rands::>(); + a - b + } + + pub fn gen_binary_zero_share(&mut self) -> RingElement + where + Standard: Distribution, + { + let (a, b) = self.gen_rands::>(); + a ^ b + } +} diff --git a/iris-mpc-cpu/src/shares/bit.rs b/iris-mpc-cpu/src/shares/bit.rs new file mode 100644 index 000000000..44f2bbb95 --- /dev/null +++ b/iris-mpc-cpu/src/shares/bit.rs @@ -0,0 +1,391 @@ +use eyre::{eyre, Error}; +use num_traits::{ + AsPrimitive, One, WrappingAdd, WrappingMul, WrappingNeg, WrappingShl, WrappingShr, WrappingSub, + Zero, +}; +use rand::{distributions::Standard, prelude::Distribution, Rng}; +use serde::{Deserialize, Serialize}; +use std::ops::{ + Add, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Mul, Neg, Not, Rem, Shl, + Shr, Sub, +}; + +/// Bit is a sharable wrapper for a boolean value +#[derive( + Copy, + Clone, + Debug, + Default, + Eq, + PartialEq, + PartialOrd, + Ord, + Serialize, + Deserialize, + bytemuck::NoUninit, + bytemuck::AnyBitPattern, +)] +#[repr(transparent)] +/// This transparent is important due to some typecasts! +pub struct Bit(pub(super) u8); + +impl AsPrimitive for Bit { + fn as_(self) -> Self { + self + } +} + +impl std::fmt::Display for Bit { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.0 { + 1 => write!(f, "1"), + 0 => write!(f, "0"), + _ => unreachable!(), + } + } +} + +impl Bit { + pub fn new(value: bool) -> Self { + Self(value as u8) + } + + pub fn convert(self) -> bool { + debug_assert!(self.0 == 0 || self.0 == 1); + self.0 == 1 + } +} + +impl TryFrom for Bit { + type Error = Error; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(Bit(0)), + 1 => Ok(Bit(1)), + _ => Err(eyre!("Conversion Error Bit From u8")), + } + } +} + +impl TryFrom for Bit { + type Error = Error; + + fn try_from(value: usize) -> Result { + match value { + 0 => Ok(Bit(0)), + 1 => Ok(Bit(1)), + _ => Err(eyre!("Conversion error Bit from usize")), + } + } +} + +impl Add for Bit { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + #[inline(always)] + fn add(self, rhs: Self) -> Self::Output { + self ^ rhs + } +} + +impl Add<&Bit> for Bit { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + #[inline(always)] + fn add(self, rhs: &Self) -> Self::Output { + self ^ rhs + } +} + +impl WrappingAdd for Bit { + #[inline(always)] + fn wrapping_add(&self, rhs: &Self) -> Self::Output { + *self ^ *rhs + } +} + +impl Sub for Bit { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + #[inline(always)] + fn sub(self, rhs: Self) -> Self::Output { + self ^ rhs + } +} + +impl Sub<&Bit> for Bit { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + #[inline(always)] + fn sub(self, rhs: &Self) -> Self::Output { + self ^ rhs + } +} + +impl WrappingSub for Bit { + #[inline(always)] + fn wrapping_sub(&self, rhs: &Self) -> Self::Output { + *self ^ *rhs + } +} + +impl Neg for Bit { + type Output = Self; + #[inline(always)] + fn neg(self) -> Self::Output { + self + } +} + +impl WrappingNeg for Bit { + #[inline(always)] + fn wrapping_neg(&self) -> Self { + -*self + } +} + +impl BitXor for Bit { + type Output = Self; + + #[inline(always)] + fn bitxor(self, rhs: Self) -> Self::Output { + Bit(self.0 ^ rhs.0) + } +} + +impl BitXor<&Bit> for Bit { + type Output = Self; + + #[inline(always)] + fn bitxor(self, rhs: &Self) -> Self::Output { + Bit(self.0 ^ rhs.0) + } +} + +impl BitXorAssign for Bit { + #[inline(always)] + fn bitxor_assign(&mut self, rhs: Self) { + self.0 ^= rhs.0; + } +} + +impl BitXorAssign<&Bit> for Bit { + #[inline(always)] + fn bitxor_assign(&mut self, rhs: &Self) { + self.0 ^= rhs.0; + } +} + +impl BitOr for Bit { + type Output = Self; + + #[inline(always)] + fn bitor(self, rhs: Self) -> Self::Output { + Bit(self.0 | rhs.0) + } +} + +impl BitOr<&Bit> for Bit { + type Output = Self; + + #[inline(always)] + fn bitor(self, rhs: &Self) -> Self::Output { + Bit(self.0 | rhs.0) + } +} + +impl BitOrAssign for Bit { + #[inline(always)] + fn bitor_assign(&mut self, rhs: Self) { + self.0 |= rhs.0; + } +} + +impl BitOrAssign<&Bit> for Bit { + #[inline(always)] + fn bitor_assign(&mut self, rhs: &Self) { + self.0 |= rhs.0; + } +} + +impl Not for Bit { + type Output = Self; + + #[inline(always)] + fn not(self) -> Self { + Self(self.0 ^ 1) + } +} + +impl BitAnd for Bit { + type Output = Self; + + #[inline(always)] + fn bitand(self, rhs: Self) -> Self::Output { + Bit(self.0 & rhs.0) + } +} + +impl BitAnd<&Bit> for Bit { + type Output = Self; + + #[inline(always)] + fn bitand(self, rhs: &Self) -> Self::Output { + Bit(self.0 & rhs.0) + } +} + +impl BitAndAssign for Bit { + #[inline(always)] + fn bitand_assign(&mut self, rhs: Self) { + self.0 &= rhs.0; + } +} + +impl BitAndAssign<&Bit> for Bit { + #[inline(always)] + fn bitand_assign(&mut self, rhs: &Self) { + self.0 &= rhs.0; + } +} + +impl Mul for Bit { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + #[inline(always)] + fn mul(self, rhs: Self) -> Self::Output { + self & rhs + } +} + +impl Mul<&Bit> for Bit { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + #[inline(always)] + fn mul(self, rhs: &Self) -> Self::Output { + self & rhs + } +} + +impl WrappingMul for Bit { + #[inline(always)] + fn wrapping_mul(&self, rhs: &Self) -> Self::Output { + *self & *rhs + } +} + +impl Zero for Bit { + #[inline(always)] + fn zero() -> Self { + Self(0) + } + + #[inline(always)] + fn is_zero(&self) -> bool { + self.0 == 0 + } +} + +impl One for Bit { + #[inline(always)] + fn one() -> Self { + Self(1) + } +} + +impl From for u8 { + #[inline(always)] + fn from(other: Bit) -> Self { + other.0 + } +} + +impl From for Bit { + #[inline(always)] + fn from(other: bool) -> Self { + Bit(other as u8) + } +} + +impl From for bool { + #[inline(always)] + fn from(other: Bit) -> Self { + other.0 == 1 + } +} + +impl Shl for Bit { + type Output = Self; + + fn shl(self, rhs: usize) -> Self { + if rhs == 0 { + self + } else { + Self(0) + } + } +} + +impl WrappingShl for Bit { + #[inline(always)] + fn wrapping_shl(&self, rhs: u32) -> Self { + *self << rhs as usize + } +} + +impl Shr for Bit { + type Output = Self; + + fn shr(self, rhs: usize) -> Self { + if rhs == 0 { + self + } else { + Self(0) + } + } +} + +impl WrappingShr for Bit { + #[inline(always)] + fn wrapping_shr(&self, rhs: u32) -> Self { + *self >> rhs as usize + } +} + +impl Distribution for Standard { + #[inline(always)] + fn sample(&self, rng: &mut R) -> Bit { + Bit(rng.gen::() as u8) + } +} + +impl AsRef for Bit { + fn as_ref(&self) -> &Bit { + self + } +} + +impl From for u128 { + fn from(val: Bit) -> Self { + u128::from(val.0) + } +} + +impl Rem for Bit { + type Output = Self; + + fn rem(self, rhs: Self) -> Self::Output { + match rhs { + Bit(0) => panic!("Division by zero"), + Bit(1) => Bit(0), + _ => unreachable!(), + } + } +} diff --git a/iris-mpc-cpu/src/shares/int_ring.rs b/iris-mpc-cpu/src/shares/int_ring.rs new file mode 100644 index 000000000..5e5da3a37 --- /dev/null +++ b/iris-mpc-cpu/src/shares/int_ring.rs @@ -0,0 +1,120 @@ +use super::bit::Bit; +use num_traits::{ + AsPrimitive, One, WrappingAdd, WrappingMul, WrappingNeg, WrappingShl, WrappingShr, WrappingSub, + Zero, +}; +use serde::{Deserialize, Serialize}; +use std::{ + fmt::Debug, + ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Neg, Not, Rem}, +}; + +pub trait IntRing2k: + std::fmt::Display + + Serialize + + for<'a> Deserialize<'a> + + Default + + WrappingAdd + + WrappingSub + + WrappingMul + + WrappingNeg + + WrappingShl + + WrappingShr + + Not + + BitXor + + BitAnd + + BitOr + + BitXorAssign + + BitAndAssign + + BitOrAssign + + PartialEq + + From + + Into + + Copy + + Debug + + Zero + + One + + Sized + + Send + + Sync + + Rem + + 'static + + bytemuck::NoUninit + + bytemuck::AnyBitPattern +{ + type Signed: Neg + From + AsPrimitive; + const K: usize; + const BYTES: usize; + + /// a += b + #[inline(always)] + fn wrapping_add_assign(&mut self, rhs: &Self) { + *self = self.wrapping_add(rhs); + } + + /// a -= b + #[inline(always)] + fn wrapping_sub_assign(&mut self, rhs: &Self) { + *self = self.wrapping_sub(rhs); + } + + /// a = -a + #[inline(always)] + fn wrapping_neg_inplace(&mut self) { + *self = self.wrapping_neg(); + } + + /// a*= b + #[inline(always)] + fn wrapping_mul_assign(&mut self, rhs: &Self) { + *self = self.wrapping_mul(rhs); + } + + /// a <<= b + #[inline(always)] + fn wrapping_shl_assign(&mut self, rhs: u32) { + *self = self.wrapping_shl(rhs); + } + + /// a >>= b + #[inline(always)] + fn wrapping_shr_assign(&mut self, rhs: u32) { + *self = self.wrapping_shr(rhs); + } +} + +impl IntRing2k for Bit { + type Signed = Bit; + const K: usize = 1; + const BYTES: usize = 1; +} + +impl IntRing2k for u8 { + type Signed = i8; + const K: usize = Self::BITS as usize; + const BYTES: usize = Self::K / 8; +} + +impl IntRing2k for u16 { + type Signed = i16; + const K: usize = Self::BITS as usize; + const BYTES: usize = Self::K / 8; +} + +impl IntRing2k for u32 { + type Signed = i32; + const K: usize = Self::BITS as usize; + const BYTES: usize = Self::K / 8; +} + +impl IntRing2k for u64 { + type Signed = i64; + const K: usize = Self::BITS as usize; + const BYTES: usize = Self::K / 8; +} + +impl IntRing2k for u128 { + type Signed = i128; + const K: usize = Self::BITS as usize; + const BYTES: usize = Self::K / 8; +} diff --git a/iris-mpc-cpu/src/shares/mod.rs b/iris-mpc-cpu/src/shares/mod.rs new file mode 100644 index 000000000..45a424f60 --- /dev/null +++ b/iris-mpc-cpu/src/shares/mod.rs @@ -0,0 +1,6 @@ +pub(crate) mod bit; +pub(crate) mod int_ring; +pub(crate) mod ring_impl; +pub(crate) mod share; +pub(crate) mod vecshare; +pub(crate) mod vecshare_bittranspose; diff --git a/iris-mpc-cpu/src/shares/ring_impl.rs b/iris-mpc-cpu/src/shares/ring_impl.rs new file mode 100644 index 000000000..9ec197ab8 --- /dev/null +++ b/iris-mpc-cpu/src/shares/ring_impl.rs @@ -0,0 +1,451 @@ +use super::{bit::Bit, int_ring::IntRing2k}; +use num_traits::{One, Zero}; +use rand::{ + distributions::{Distribution, Standard}, + Rng, +}; +use serde::{Deserialize, Serialize}; +use std::{ + marker::PhantomData, + mem::ManuallyDrop, + ops::{ + Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Mul, + MulAssign, Neg, Not, Rem, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, + }, +}; + +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize, PartialOrd, Eq, Ord)] +#[serde(bound = "")] +#[repr(transparent)] +pub struct RingElement(pub T); + +pub struct BitIter<'a, T: IntRing2k> { + bits: &'a RingElement, + index: usize, + _marker: std::marker::PhantomData, +} + +impl Iterator for BitIter<'_, T> { + type Item = Bit; + + fn next(&mut self) -> Option { + if self.index >= T::K { + None + } else { + let bit = ((self.bits.0 >> self.index) & T::one()) == T::one(); + self.index += 1; + Some(Bit(bit as u8)) + } + } + + fn size_hint(&self) -> (usize, Option) { + let len = T::K - self.index; + (len, Some(len)) + } +} + +impl RingElement { + /// Safe because RingElement has repr(transparent) + pub fn convert_slice(vec: &[Self]) -> &[T] { + // SAFETY: RingElement has repr(transparent) + unsafe { &*(vec as *const [Self] as *const [T]) } + } + + /// Safe because RingElement has repr(transparent) + pub fn convert_vec(vec: Vec) -> Vec { + let me = ManuallyDrop::new(vec); + // SAFETY: RingElement has repr(transparent) + unsafe { Vec::from_raw_parts(me.as_ptr() as *mut T, me.len(), me.capacity()) } + } + + /// Safe because RingElement has repr(transparent) + pub fn convert_slice_rev(vec: &[T]) -> &[Self] { + // SAFETY: RingElement has repr(transparent) + unsafe { &*(vec as *const [T] as *const [Self]) } + } + + /// Safe because RingElement has repr(transparent) + pub fn convert_vec_rev(vec: Vec) -> Vec { + let me = ManuallyDrop::new(vec); + // SAFETY: RingElement has repr(transparent) + unsafe { Vec::from_raw_parts(me.as_ptr() as *mut Self, me.len(), me.capacity()) } + } + + pub fn convert(self) -> T { + self.0 + } + + pub(crate) fn bit_iter(&self) -> BitIter<'_, T> { + BitIter { + bits: self, + index: 0, + _marker: PhantomData, + } + } + + pub fn get_bit(&self, index: usize) -> Self { + RingElement((self.0 >> index) & T::one()) + } + + pub(crate) fn get_bit_as_bit(&self, index: usize) -> RingElement { + let bit = ((self.0 >> index) & T::one()) == T::one(); + RingElement(Bit(bit as u8)) + } + + pub fn upgrade_to_128(self) -> RingElement { + RingElement(self.0.into()) + } +} + +impl std::fmt::Display for RingElement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&self.0, f) + } +} + +impl Add for RingElement { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self(self.0.wrapping_add(&rhs.0)) + } +} + +impl Add<&Self> for RingElement { + type Output = Self; + + fn add(self, rhs: &Self) -> Self::Output { + Self(self.0.wrapping_add(&rhs.0)) + } +} + +impl AddAssign for RingElement { + fn add_assign(&mut self, rhs: Self) { + self.0.wrapping_add_assign(&rhs.0) + } +} + +impl AddAssign<&Self> for RingElement { + fn add_assign(&mut self, rhs: &Self) { + self.0.wrapping_add_assign(&rhs.0) + } +} + +impl Sub for RingElement { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0.wrapping_sub(&rhs.0)) + } +} + +impl Sub<&Self> for RingElement { + type Output = Self; + + fn sub(self, rhs: &Self) -> Self::Output { + Self(self.0.wrapping_sub(&rhs.0)) + } +} + +impl SubAssign for RingElement { + fn sub_assign(&mut self, rhs: Self) { + self.0.wrapping_sub_assign(&rhs.0) + } +} + +impl SubAssign<&Self> for RingElement { + fn sub_assign(&mut self, rhs: &Self) { + self.0.wrapping_sub_assign(&rhs.0) + } +} + +impl Mul for RingElement { + type Output = Self; + + fn mul(self, rhs: T) -> Self::Output { + Self(self.0.wrapping_mul(&rhs)) + } +} + +impl Mul<&T> for RingElement { + type Output = Self; + + fn mul(self, rhs: &T) -> Self::Output { + Self(self.0.wrapping_mul(rhs)) + } +} + +impl Mul for RingElement { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.wrapping_mul(&rhs.0)) + } +} + +impl Mul<&Self> for RingElement { + type Output = Self; + + fn mul(self, rhs: &Self) -> Self::Output { + Self(self.0.wrapping_mul(&rhs.0)) + } +} + +impl MulAssign for RingElement { + fn mul_assign(&mut self, rhs: Self) { + self.0.wrapping_mul_assign(&rhs.0) + } +} + +impl MulAssign<&Self> for RingElement { + fn mul_assign(&mut self, rhs: &Self) { + self.0.wrapping_mul_assign(&rhs.0) + } +} + +impl MulAssign for RingElement { + fn mul_assign(&mut self, rhs: T) { + self.0.wrapping_mul_assign(&rhs) + } +} + +impl MulAssign<&T> for RingElement { + fn mul_assign(&mut self, rhs: &T) { + self.0.wrapping_mul_assign(rhs) + } +} + +impl Zero for RingElement { + fn zero() -> Self { + Self(T::zero()) + } + + fn is_zero(&self) -> bool { + self.0.is_zero() + } +} + +impl One for RingElement { + fn one() -> Self { + Self(T::one()) + } +} + +impl Neg for RingElement { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(self.0.wrapping_neg()) + } +} + +impl Distribution> for Standard +where + Standard: Distribution, +{ + #[inline(always)] + fn sample(&self, rng: &mut R) -> RingElement { + RingElement(rng.gen()) + } +} + +impl Not for RingElement { + type Output = Self; + + fn not(self) -> Self { + Self(!self.0) + } +} + +impl BitXor for RingElement { + type Output = Self; + + fn bitxor(self, rhs: Self) -> Self::Output { + RingElement(self.0 ^ rhs.0) + } +} + +impl BitXor<&Self> for RingElement { + type Output = Self; + + fn bitxor(self, rhs: &Self) -> Self::Output { + RingElement(self.0 ^ rhs.0) + } +} + +impl BitXorAssign for RingElement { + fn bitxor_assign(&mut self, rhs: Self) { + self.0 ^= rhs.0; + } +} + +impl BitXorAssign<&Self> for RingElement { + fn bitxor_assign(&mut self, rhs: &Self) { + self.0 ^= rhs.0; + } +} + +impl BitOr for RingElement { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + RingElement(self.0 | rhs.0) + } +} + +impl BitOr<&Self> for RingElement { + type Output = Self; + + fn bitor(self, rhs: &Self) -> Self::Output { + RingElement(self.0 | rhs.0) + } +} + +impl BitOrAssign for RingElement { + fn bitor_assign(&mut self, rhs: Self) { + self.0 |= rhs.0; + } +} + +impl BitOrAssign<&Self> for RingElement { + fn bitor_assign(&mut self, rhs: &Self) { + self.0 |= rhs.0; + } +} + +impl BitAnd for RingElement { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self::Output { + RingElement(self.0 & rhs.0) + } +} + +impl BitAnd for RingElement { + type Output = Self; + + fn bitand(self, rhs: T) -> Self::Output { + RingElement(self.0 & rhs) + } +} + +impl BitAnd<&Self> for RingElement { + type Output = Self; + + fn bitand(self, rhs: &Self) -> Self::Output { + RingElement(self.0 & rhs.0) + } +} + +impl BitAndAssign for RingElement { + fn bitand_assign(&mut self, rhs: Self) { + self.0 &= rhs.0; + } +} + +impl BitAndAssign<&Self> for RingElement { + fn bitand_assign(&mut self, rhs: &Self) { + self.0 &= rhs.0; + } +} + +impl Shl for RingElement { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + RingElement(self.0.wrapping_shl(rhs)) + } +} + +impl ShlAssign for RingElement { + fn shl_assign(&mut self, rhs: u32) { + self.0.wrapping_shl_assign(rhs) + } +} + +impl Shr for RingElement { + type Output = Self; + + fn shr(self, rhs: u32) -> Self::Output { + RingElement(self.0.wrapping_shr(rhs)) + } +} + +impl ShrAssign for RingElement { + fn shr_assign(&mut self, rhs: u32) { + self.0.wrapping_shr_assign(rhs) + } +} + +impl Rem for RingElement { + type Output = Self; + + fn rem(self, rhs: T) -> Self::Output { + RingElement(self.0 % rhs) + } +} + +#[cfg(test)] +mod unsafe_test { + use super::*; + use aes_prng::AesRng; + use rand::{Rng, SeedableRng}; + + const ELEMENTS: usize = 100; + + fn conversion_test() + where + Standard: Distribution, + { + let mut rng = AesRng::from_entropy(); + let t_vec: Vec = (0..ELEMENTS).map(|_| rng.gen()).collect(); + let rt_vec: Vec> = + (0..ELEMENTS).map(|_| rng.gen::>()).collect(); + + // Convert vec to vec> + let t_conv = RingElement::convert_vec_rev(t_vec.to_owned()); + assert_eq!(t_conv.len(), t_vec.len()); + for (a, b) in t_conv.iter().zip(t_vec.iter()) { + assert_eq!(a.0, *b) + } + + // Convert slice vec to vec> + let t_conv = RingElement::convert_slice_rev(&t_vec); + assert_eq!(t_conv.len(), t_vec.len()); + for (a, b) in t_conv.iter().zip(t_vec.iter()) { + assert_eq!(a.0, *b) + } + + // Convert vec> to vec + let rt_conv = RingElement::convert_vec(rt_vec.to_owned()); + assert_eq!(rt_conv.len(), rt_vec.len()); + for (a, b) in rt_conv.iter().zip(rt_vec.iter()) { + assert_eq!(*a, b.0) + } + + // Convert slice vec> to vec + let rt_conv = RingElement::convert_slice(&rt_vec); + assert_eq!(rt_conv.len(), rt_vec.len()); + for (a, b) in rt_conv.iter().zip(rt_vec.iter()) { + assert_eq!(*a, b.0) + } + } + + macro_rules! test_impl { + ($([$ty:ty,$fn:ident]),*) => ($( + #[test] + fn $fn() { + conversion_test::<$ty>(); + } + )*) + } + + test_impl! { + [Bit, bit_test], + [u8, u8_test], + [u16, u16_test], + [u32, u32_test], + [u64, u64_test], + [u128, u128_test] + } +} diff --git a/iris-mpc-cpu/src/shares/share.rs b/iris-mpc-cpu/src/shares/share.rs new file mode 100644 index 000000000..60ec555b9 --- /dev/null +++ b/iris-mpc-cpu/src/shares/share.rs @@ -0,0 +1,317 @@ +use super::{int_ring::IntRing2k, ring_impl::RingElement}; +use iris_mpc_common::id::PartyID; +use num_traits::Zero; +use serde::{Deserialize, Serialize}; +use std::ops::{ + Add, AddAssign, BitAnd, BitXor, BitXorAssign, Mul, MulAssign, Neg, Not, Shl, Shr, Sub, + SubAssign, +}; + +#[derive(Clone, Debug, PartialEq, Default, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct Share { + pub a: RingElement, + pub b: RingElement, +} + +impl Share { + pub fn new(a: RingElement, b: RingElement) -> Self { + Self { a, b } + } + + pub(crate) fn sub_from_const(&self, other: T, id: PartyID) -> Self { + let mut a = -self; + a.add_assign_const(other, id); + a + } + + pub fn add_assign_const(&mut self, other: T, id: PartyID) { + match id { + PartyID::ID0 => self.a += RingElement(other), + PartyID::ID1 => self.b += RingElement(other), + PartyID::ID2 => {} + } + } + + pub fn get_a(self) -> RingElement { + self.a + } + + pub fn get_b(self) -> RingElement { + self.b + } + + pub fn get_ab(self) -> (RingElement, RingElement) { + (self.a, self.b) + } + + pub fn get_ab_ref(&self) -> (RingElement, RingElement) { + (self.a, self.b) + } +} + +impl Add<&Self> for Share { + type Output = Self; + + fn add(self, rhs: &Self) -> Self::Output { + Share { + a: self.a + rhs.a, + b: self.b + rhs.b, + } + } +} + +impl Add for Share { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Share { + a: self.a + rhs.a, + b: self.b + rhs.b, + } + } +} + +impl Sub for Share { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Share { + a: self.a - rhs.a, + b: self.b - rhs.b, + } + } +} + +impl Sub<&Self> for Share { + type Output = Self; + + fn sub(self, rhs: &Self) -> Self::Output { + Share { + a: self.a - rhs.a, + b: self.b - rhs.b, + } + } +} + +impl AddAssign for Share { + fn add_assign(&mut self, rhs: Self) { + self.a += rhs.a; + self.b += rhs.b; + } +} + +impl AddAssign<&Self> for Share { + fn add_assign(&mut self, rhs: &Self) { + self.a += rhs.a; + self.b += rhs.b; + } +} + +impl SubAssign for Share { + fn sub_assign(&mut self, rhs: Self) { + self.a -= rhs.a; + self.b -= rhs.b; + } +} + +impl SubAssign<&Self> for Share { + fn sub_assign(&mut self, rhs: &Self) { + self.a -= rhs.a; + self.b -= rhs.b; + } +} + +impl Mul> for Share { + type Output = Self; + + fn mul(self, rhs: RingElement) -> Self::Output { + Share { + a: self.a * rhs, + b: self.b * rhs, + } + } +} + +impl Mul for Share { + type Output = Self; + + fn mul(self, rhs: T) -> Self::Output { + self * RingElement(rhs) + } +} + +impl Mul for &Share { + type Output = Share; + + fn mul(self, rhs: T) -> Self::Output { + Share { + a: self.a * rhs, + b: self.b * rhs, + } + } +} + +impl MulAssign for Share { + fn mul_assign(&mut self, rhs: T) { + self.a *= rhs; + self.b *= rhs; + } +} + +/// This is only the local part of the multiplication (so without randomness and +/// without communication)! +impl Mul for &Share { + type Output = RingElement; + + fn mul(self, rhs: Self) -> Self::Output { + self.a * rhs.a + self.b * rhs.a + self.a * rhs.b + } +} + +impl BitXor for &Share { + type Output = Share; + + fn bitxor(self, rhs: Self) -> Self::Output { + Share { + a: self.a ^ rhs.a, + b: self.b ^ rhs.b, + } + } +} + +impl BitXor for Share { + type Output = Self; + + fn bitxor(self, rhs: Self) -> Self::Output { + Share { + a: self.a ^ rhs.a, + b: self.b ^ rhs.b, + } + } +} + +impl BitXor<&Self> for Share { + type Output = Self; + + fn bitxor(self, rhs: &Self) -> Self::Output { + Share { + a: self.a ^ rhs.a, + b: self.b ^ rhs.b, + } + } +} + +impl BitXorAssign<&Self> for Share { + fn bitxor_assign(&mut self, rhs: &Self) { + self.a ^= rhs.a; + self.b ^= rhs.b; + } +} + +impl BitXorAssign for Share { + fn bitxor_assign(&mut self, rhs: Self) { + self.a ^= rhs.a; + self.b ^= rhs.b; + } +} + +/// This is only the local part of the AND (so without randomness and without +/// communication)! +impl BitAnd for &Share { + type Output = RingElement; + + fn bitand(self, rhs: Self) -> Self::Output { + (self.a & rhs.a) ^ (self.b & rhs.a) ^ (self.a & rhs.b) + } +} + +impl BitAnd<&RingElement> for &Share { + type Output = Share; + + fn bitand(self, rhs: &RingElement) -> Self::Output { + Share { + a: self.a & rhs, + b: self.b & rhs, + } + } +} + +impl BitAnd for Share { + type Output = Share; + + fn bitand(self, rhs: T) -> Self::Output { + Share { + a: self.a & rhs, + b: self.b & rhs, + } + } +} + +impl Zero for Share { + fn zero() -> Self { + Self { + a: RingElement::zero(), + b: RingElement::zero(), + } + } + + fn is_zero(&self) -> bool { + self.a.is_zero() && self.b.is_zero() + } +} + +impl Neg for Share { + type Output = Self; + + fn neg(self) -> Self::Output { + Self { + a: -self.a, + b: -self.b, + } + } +} + +impl Neg for &Share { + type Output = Share; + + fn neg(self) -> Self::Output { + Share { + a: -self.a, + b: -self.b, + } + } +} + +impl Not for &Share { + type Output = Share; + + fn not(self) -> Self::Output { + Share { + a: !self.a, + b: !self.b, + } + } +} + +impl Shr for &Share { + type Output = Share; + + fn shr(self, rhs: u32) -> Self::Output { + Share { + a: self.a >> rhs, + b: self.b >> rhs, + } + } +} + +impl Shl for Share { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + Self { + a: self.a << rhs, + b: self.b << rhs, + } + } +} diff --git a/iris-mpc-cpu/src/shares/vecshare.rs b/iris-mpc-cpu/src/shares/vecshare.rs new file mode 100644 index 000000000..9076bd23f --- /dev/null +++ b/iris-mpc-cpu/src/shares/vecshare.rs @@ -0,0 +1,385 @@ +use super::{bit::Bit, int_ring::IntRing2k, ring_impl::RingElement, share::Share}; +use bytes::{Buf, BytesMut}; +use num_traits::Zero; +use serde::{Deserialize, Serialize}; +use std::{ + marker::PhantomData, + ops::{AddAssign, BitXor, BitXorAssign, Deref, DerefMut, Not, SubAssign}, +}; + +#[repr(transparent)] +pub struct RingBytesIter { + bytes: BytesMut, + _marker: std::marker::PhantomData, +} + +impl RingBytesIter { + pub fn new(bytes: BytesMut) -> Self { + Self { + bytes, + _marker: PhantomData, + } + } +} + +impl Iterator for RingBytesIter { + type Item = RingElement; + + fn next(&mut self) -> Option { + if self.bytes.remaining() == 0 { + None + } else { + let res = bytemuck::pod_read_unaligned(&self.bytes.chunk()[..T::BYTES]); + self.bytes.advance(T::BYTES); + Some(RingElement(res)) + } + } + + fn size_hint(&self) -> (usize, Option) { + let len = self.bytes.remaining() / T::BYTES; + (len, Some(len)) + } +} + +#[derive(Clone, Copy, Debug)] +#[repr(transparent)] +pub struct SliceShare<'a, T: IntRing2k> { + shares: &'a [Share], +} + +impl<'a, T: IntRing2k> SliceShare<'a, T> { + pub fn split_at(&self, mid: usize) -> (SliceShare, SliceShare) { + let (a, b) = self.shares.split_at(mid); + (SliceShare { shares: a }, SliceShare { shares: b }) + } + + pub fn chunks(&self, chunk_size: usize) -> impl Iterator> + '_ { + self.shares + .chunks(chunk_size) + .map(|x| SliceShare { shares: x }) + } + + pub fn len(&self) -> usize { + self.shares.len() + } + + pub fn iter(&self) -> std::slice::Iter<'_, Share> { + self.shares.iter() + } + + pub fn is_empty(&self) -> bool { + self.shares.is_empty() + } +} + +#[derive(Debug)] +#[repr(transparent)] +pub struct SliceShareMut<'a, T: IntRing2k> { + shares: &'a mut [Share], +} + +impl<'a, T: IntRing2k> SliceShareMut<'a, T> { + pub fn to_vec(&self) -> VecShare { + VecShare { + shares: self.shares.to_vec(), + } + } + + pub fn to_slice(&self) -> SliceShare { + SliceShare { + shares: self.shares, + } + } +} + +#[derive(Clone, Debug, PartialEq, Default, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[serde(bound = "")] +#[repr(transparent)] +pub struct VecShare { + pub(crate) shares: Vec>, +} + +impl VecShare { + #[cfg(test)] + pub fn new_share(share: Share) -> Self { + let shares = vec![share]; + Self { shares } + } + + pub fn new_vec(shares: Vec>) -> Self { + Self { shares } + } + + pub fn inner(self) -> Vec> { + self.shares + } + + pub fn with_capacity(capacity: usize) -> Self { + let shares = Vec::with_capacity(capacity); + Self { shares } + } + + pub fn extend(&mut self, items: Self) { + self.shares.extend(items.shares); + } + + pub fn extend_from_slice(&mut self, items: SliceShare) { + self.shares.extend_from_slice(items.shares); + } + + pub fn len(&self) -> usize { + self.shares.len() + } + + pub fn iter(&self) -> std::slice::Iter<'_, Share> { + self.shares.iter() + } + + pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, Share> { + self.shares.iter_mut() + } + + pub fn is_empty(&self) -> bool { + self.shares.is_empty() + } + + pub fn push(&mut self, el: Share) { + self.shares.push(el) + } + + pub fn pop(&mut self) -> Option> { + self.shares.pop() + } + + pub fn sum(&self) -> Share { + self.shares.iter().fold(Share::zero(), |a, b| a + b) + } + + pub fn not_inplace(&mut self) { + for x in self.shares.iter_mut() { + *x = !&*x; + } + } + + pub fn split_at(&self, mid: usize) -> (SliceShare, SliceShare) { + let (a, b) = self.shares.split_at(mid); + (SliceShare { shares: a }, SliceShare { shares: b }) + } + + pub fn split_at_mut(&mut self, mid: usize) -> (SliceShareMut, SliceShareMut) { + let (a, b) = self.shares.split_at_mut(mid); + (SliceShareMut { shares: a }, SliceShareMut { shares: b }) + } + + pub fn get_at(self, index: usize) -> Share { + self.shares[index].to_owned() + } + + pub fn from_avec_biter(a: Vec>, b: RingBytesIter) -> Self { + let shares = a + .into_iter() + .zip(b) + .map(|(a_, b_)| Share::new(a_, b_)) + .collect(); + Self { shares } + } + + pub fn from_ab(a: Vec>, b: Vec>) -> Self { + let shares = a + .into_iter() + .zip(b) + .map(|(a_, b_)| Share::new(a_, b_)) + .collect(); + Self { shares } + } + + pub fn flatten(inp: Vec) -> Self { + Self { + shares: inp.into_iter().flat_map(|x| x.shares).collect(), + } + } + + pub fn convert_to_bits(self) -> VecShare { + let mut res = VecShare::with_capacity(T::K * self.shares.len()); + for share in self.shares.into_iter() { + let (a, b) = share.get_ab(); + for (a, b) in a.bit_iter().zip(b.bit_iter()) { + res.push(Share::new(RingElement(a), RingElement(b))); + } + } + res + } + + pub fn truncate(&mut self, len: usize) { + self.shares.truncate(len); + } + + pub fn as_slice(&self) -> SliceShare { + SliceShare { + shares: &self.shares, + } + } + + pub fn as_slice_mut(&mut self) -> SliceShareMut { + SliceShareMut { + shares: &mut self.shares, + } + } +} + +impl VecShare { + pub fn pack(self) -> VecShare { + let outlen = (self.shares.len() + T::K - 1) / T::K; + let mut out = VecShare::with_capacity(outlen); + + for a_ in self.shares.chunks(T::K) { + let mut share_a = RingElement::::zero(); + let mut share_b = RingElement::::zero(); + for (i, bit) in a_.iter().enumerate() { + let (bit_a, bit_b) = bit.to_owned().get_ab(); + share_a |= RingElement(T::from(bit_a.convert().convert()) << i); + share_b |= RingElement(T::from(bit_b.convert().convert()) << i); + } + let share = Share::new(share_a, share_b); + out.push(share); + } + + out + } + + pub fn from_share(share: Share) -> Self { + let (a, b) = share.get_ab(); + let mut res = VecShare::with_capacity(T::K); + for (a, b) in a.bit_iter().zip(b.bit_iter()) { + res.push(Share::new(RingElement(a), RingElement(b))); + } + res + } +} + +impl IntoIterator for VecShare { + type Item = Share; + type IntoIter = std::vec::IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + self.shares.into_iter() + } +} + +impl Not for SliceShare<'_, T> { + type Output = VecShare; + + fn not(self) -> Self::Output { + let mut v = VecShare::with_capacity(self.shares.len()); + for x in self.shares.iter() { + v.push(!x); + } + v + } +} + +impl BitXor for SliceShare<'_, T> { + type Output = VecShare; + + fn bitxor(self, rhs: Self) -> Self::Output { + debug_assert_eq!(self.shares.len(), rhs.shares.len()); + let mut v = VecShare::with_capacity(self.shares.len()); + for (x1, x2) in self.shares.iter().zip(rhs.shares.iter()) { + v.push(x1 ^ x2); + } + v + } +} + +impl BitXor for VecShare { + type Output = Self; + + fn bitxor(self, rhs: Self) -> Self::Output { + debug_assert_eq!(self.shares.len(), rhs.shares.len()); + let mut v = VecShare::with_capacity(self.shares.len()); + for (x1, x2) in self.shares.into_iter().zip(rhs.shares) { + v.push(x1 ^ x2); + } + v + } +} + +impl BitXor> for VecShare { + type Output = Self; + + fn bitxor(self, rhs: SliceShare<'_, T>) -> Self::Output { + debug_assert_eq!(self.shares.len(), rhs.shares.len()); + let mut v = VecShare::with_capacity(self.shares.len()); + for (x1, x2) in self.shares.into_iter().zip(rhs.shares.iter()) { + v.push(x1 ^ x2); + } + v + } +} + +impl AddAssign> for VecShare { + fn add_assign(&mut self, rhs: SliceShare<'_, T>) { + debug_assert_eq!(self.shares.len(), rhs.shares.len()); + for (x1, x2) in self.shares.iter_mut().zip(rhs.shares.iter()) { + *x1 += x2; + } + } +} + +impl SubAssign> for VecShare { + fn sub_assign(&mut self, rhs: SliceShare<'_, T>) { + debug_assert_eq!(self.shares.len(), rhs.shares.len()); + for (x1, x2) in self.shares.iter_mut().zip(rhs.shares.iter()) { + *x1 -= x2; + } + } +} + +impl SubAssign for VecShare { + fn sub_assign(&mut self, rhs: Self) { + debug_assert_eq!(self.shares.len(), rhs.shares.len()); + for (x1, x2) in self.shares.iter_mut().zip(rhs.shares.into_iter()) { + *x1 -= x2; + } + } +} + +impl BitXorAssign> for VecShare { + fn bitxor_assign(&mut self, rhs: SliceShare<'_, T>) { + debug_assert_eq!(self.shares.len(), rhs.shares.len()); + for (x1, x2) in self.shares.iter_mut().zip(rhs.shares.iter()) { + *x1 ^= x2; + } + } +} + +impl BitXorAssign for VecShare { + fn bitxor_assign(&mut self, rhs: Self) { + debug_assert_eq!(self.shares.len(), rhs.shares.len()); + for (x1, x2) in self.shares.iter_mut().zip(rhs.shares) { + *x1 ^= x2; + } + } +} + +impl<'a, T: IntRing2k> Deref for SliceShare<'a, T> { + type Target = [Share]; + + fn deref(&self) -> &Self::Target { + self.shares + } +} + +impl<'a, T: IntRing2k> Deref for SliceShareMut<'a, T> { + type Target = [Share]; + + fn deref(&self) -> &Self::Target { + self.shares + } +} + +impl<'a, T: IntRing2k> DerefMut for SliceShareMut<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.shares + } +} diff --git a/iris-mpc-cpu/src/shares/vecshare_bittranspose.rs b/iris-mpc-cpu/src/shares/vecshare_bittranspose.rs new file mode 100644 index 000000000..15b643fb7 --- /dev/null +++ b/iris-mpc-cpu/src/shares/vecshare_bittranspose.rs @@ -0,0 +1,426 @@ +use super::{ring_impl::RingElement, share::Share, vecshare::VecShare}; + +impl VecShare { + fn share64_from_share16s( + a: &Share, + b: &Share, + c: &Share, + d: &Share, + ) -> Share { + let a_ = (a.a.0 as u64) + | ((b.a.0 as u64) << 16) + | ((c.a.0 as u64) << 32) + | ((d.a.0 as u64) << 48); + let b_ = (a.b.0 as u64) + | ((b.b.0 as u64) << 16) + | ((c.b.0 as u64) << 32) + | ((d.b.0 as u64) << 48); + + Share { + a: RingElement(a_), + b: RingElement(b_), + } + } + + #[allow(clippy::too_many_arguments)] + fn share128_from_share16s( + a: &Share, + b: &Share, + c: &Share, + d: &Share, + e: &Share, + f: &Share, + g: &Share, + h: &Share, + ) -> Share { + let a_ = (a.a.0 as u128) + | ((b.a.0 as u128) << 16) + | ((c.a.0 as u128) << 32) + | ((d.a.0 as u128) << 48) + | ((e.a.0 as u128) << 64) + | ((f.a.0 as u128) << 80) + | ((g.a.0 as u128) << 96) + | ((h.a.0 as u128) << 112); + let b_ = (a.b.0 as u128) + | ((b.b.0 as u128) << 16) + | ((c.b.0 as u128) << 32) + | ((d.b.0 as u128) << 48) + | ((e.b.0 as u128) << 64) + | ((f.b.0 as u128) << 80) + | ((g.b.0 as u128) << 96) + | ((h.b.0 as u128) << 112); + + Share { + a: RingElement(a_), + b: RingElement(b_), + } + } + + fn share_transpose16x128(a: &[Share; 128]) -> [Share; 16] { + let mut j: u32; + let mut k: usize; + let mut m: u128; + let mut t: Share; + + let mut res = core::array::from_fn(|_| Share::default()); + + // pack results into Share128 datatypes + for (i, bb) in res.iter_mut().enumerate() { + *bb = Self::share128_from_share16s( + &a[i], + &a[i + 16], + &a[i + 32], + &a[i + 48], + &a[i + 64], + &a[i + 80], + &a[i + 96], + &a[i + 112], + ); + } + + // version of 128x128 transpose that only does the swaps needed for 16 bits + m = 0x00ff00ff00ff00ff00ff00ff00ff00ff; + j = 8; + while j != 0 { + k = 0; + while k < 16 { + t = ((&res[k] >> j) ^ &res[k + j as usize]) & m; + res[k + j as usize] ^= &t; + res[k] ^= t << j; + k = (k + j as usize + 1) & !(j as usize); + } + j >>= 1; + m = m ^ (m << j); + } + + res + } + + fn share_transpose16x64(a: &[Share; 64]) -> [Share; 16] { + let mut j: u32; + let mut k: usize; + let mut m: u64; + let mut t: Share; + + let mut res = core::array::from_fn(|_| Share::default()); + + // pack results into Share64 datatypes + for (i, bb) in res.iter_mut().enumerate() { + *bb = Self::share64_from_share16s(&a[i], &a[16 + i], &a[32 + i], &a[48 + i]); + } + + // version of 64x64 transpose that only does the swaps needed for 16 bits + m = 0x00ff00ff00ff00ff; + j = 8; + while j != 0 { + k = 0; + while k < 16 { + t = ((&res[k] >> j) ^ &res[k + j as usize]) & m; + res[k + j as usize] ^= &t; + res[k] ^= t << j; + k = (k + j as usize + 1) & !(j as usize); + } + j >>= 1; + m = m ^ (m << j); + } + + res + } + + pub fn transpose_pack_u64(self) -> Vec> { + self.transpose_pack_u64_with_len::<{ u16::BITS as usize }>() + } + + pub fn transpose_pack_u64_with_len(mut self) -> Vec> { + // Pad to multiple of 64 + let len = (self.shares.len() + 63) / 64; + self.shares.resize(len * 64, Share::default()); + + let mut res = (0..L) + .map(|_| VecShare::new_vec(vec![Share::default(); len])) + .collect::>(); + + for (j, x) in self.shares.chunks_exact(64).enumerate() { + let trans = Self::share_transpose16x64(x.try_into().unwrap()); + for (src, des) in trans.into_iter().zip(res.iter_mut()) { + des.shares[j] = src; + } + } + debug_assert_eq!(res.len(), L); + res + } + + pub fn transpose_pack_u128(self) -> Vec> { + self.transpose_pack_u128_with_len::<{ u16::BITS as usize }>() + } + + pub fn transpose_pack_u128_with_len(mut self) -> Vec> { + // Pad to multiple of 128 + let len = (self.shares.len() + 127) / 128; + self.shares.resize(len * 128, Share::default()); + + let mut res = (0..L) + .map(|_| VecShare::new_vec(vec![Share::default(); len])) + .collect::>(); + + for (j, x) in self.shares.chunks_exact(128).enumerate() { + let trans = Self::share_transpose16x128(x.try_into().unwrap()); + for (src, des) in trans.into_iter().zip(res.iter_mut()) { + des.shares[j] = src; + } + } + debug_assert_eq!(res.len(), L); + res + } +} + +impl VecShare { + fn share64_from_share32s(a: &Share, b: &Share) -> Share { + let a_ = (a.a.0 as u64) | ((b.a.0 as u64) << 32); + let b_ = (a.b.0 as u64) | ((b.b.0 as u64) << 32); + + Share { + a: RingElement(a_), + b: RingElement(b_), + } + } + + fn share128_from_share32s( + a: &Share, + b: &Share, + c: &Share, + d: &Share, + ) -> Share { + let a_ = (a.a.0 as u128) + | ((b.a.0 as u128) << 32) + | ((c.a.0 as u128) << 64) + | ((d.a.0 as u128) << 96); + let b_ = (a.b.0 as u128) + | ((b.b.0 as u128) << 32) + | ((c.b.0 as u128) << 64) + | ((d.b.0 as u128) << 96); + + Share { + a: RingElement(a_), + b: RingElement(b_), + } + } + + fn share_transpose32x128(a: &[Share; 128]) -> [Share; 32] { + let mut j: u32; + let mut k: usize; + let mut m: u128; + let mut t: Share; + + let mut res = core::array::from_fn(|_| Share::default()); + + // pack results into Share128 datatypes + for (i, bb) in res.iter_mut().enumerate() { + *bb = Self::share128_from_share32s(&a[i], &a[32 + i], &a[64 + i], &a[96 + i]); + } + + // version of 128x128 transpose that only does the swaps needed for 32 bits + m = 0x0000ffff0000ffff0000ffff0000ffff; + j = 16; + while j != 0 { + k = 0; + while k < 32 { + t = ((&res[k] >> j) ^ &res[k + j as usize]) & m; + res[k + j as usize] ^= &t; + res[k] ^= t << j; + k = (k + j as usize + 1) & !(j as usize); + } + j >>= 1; + m = m ^ (m << j); + } + + res + } + + fn share_transpose32x64(a: &[Share; 64]) -> [Share; 32] { + let mut j: u32; + let mut k: usize; + let mut m: u64; + let mut t: Share; + + let mut res = core::array::from_fn(|_| Share::default()); + + // pack results into Share64 datatypes + for (i, bb) in res.iter_mut().enumerate() { + *bb = Self::share64_from_share32s(&a[i], &a[32 + i]); + } + + // version of 64x64 transpose that only does the swaps needed for 32 bits + m = 0x0000ffff0000ffff; + j = 16; + while j != 0 { + k = 0; + while k < 32 { + t = ((&res[k] >> j) ^ &res[k + j as usize]) & m; + res[k + j as usize] ^= &t; + res[k] ^= t << j; + k = (k + j as usize + 1) & !(j as usize); + } + j >>= 1; + m = m ^ (m << j); + } + + res + } + + pub fn transpose_pack_u64(self) -> Vec> { + self.transpose_pack_u64_with_len::<{ u32::BITS as usize }>() + } + + pub fn transpose_pack_u64_with_len(mut self) -> Vec> { + // Pad to multiple of 64 + let len = (self.shares.len() + 63) / 64; + self.shares.resize(len * 64, Share::default()); + + let mut res = (0..L) + .map(|_| VecShare::new_vec(vec![Share::default(); len])) + .collect::>(); + + for (j, x) in self.shares.chunks_exact(64).enumerate() { + let trans = Self::share_transpose32x64(x.try_into().unwrap()); + for (src, des) in trans.into_iter().zip(res.iter_mut()) { + des.shares[j] = src; + } + } + debug_assert_eq!(res.len(), L); + res + } + + pub fn transpose_pack_u128(self) -> Vec> { + self.transpose_pack_u128_with_len::<{ u32::BITS as usize }>() + } + + pub fn transpose_pack_u128_with_len(mut self) -> Vec> { + // Pad to multiple of 128 + let len = (self.shares.len() + 127) / 128; + self.shares.resize(len * 128, Share::default()); + + let mut res = (0..L) + .map(|_| VecShare::new_vec(vec![Share::default(); len])) + .collect::>(); + + for (j, x) in self.shares.chunks_exact(128).enumerate() { + let trans = Self::share_transpose32x128(x.try_into().unwrap()); + for (src, des) in trans.into_iter().zip(res.iter_mut()) { + des.shares[j] = src; + } + } + debug_assert_eq!(res.len(), L); + res + } +} + +impl VecShare { + fn share128_from_share64s(a: &Share, b: &Share) -> Share { + let a_ = (a.a.0 as u128) | ((b.a.0 as u128) << 64); + let b_ = (a.b.0 as u128) | ((b.b.0 as u128) << 64); + + Share { + a: RingElement(a_), + b: RingElement(b_), + } + } + + fn share_transpose64x128(a: &[Share; 128]) -> [Share; 64] { + let mut j: u32; + let mut k: usize; + let mut m: u128; + let mut t: Share; + + let mut res = core::array::from_fn(|_| Share::default()); + + // pack results into Share128 datatypes + for (i, bb) in res.iter_mut().enumerate() { + *bb = Self::share128_from_share64s(&a[i], &a[i + 64]); + } + + // version of 128x128 transpose that only does the swaps needed for 64 bits + m = 0x00000000ffffffff00000000ffffffff; + j = 32; + while j != 0 { + k = 0; + while k < 64 { + t = ((&res[k] >> j) ^ &res[k + j as usize]) & m; + res[k + j as usize] ^= &t; + res[k] ^= t << j; + k = (k + j as usize + 1) & !(j as usize); + } + j >>= 1; + m = m ^ (m << j); + } + + res + } + + fn share_transpose64x64(a: &mut [Share; 64]) { + let mut j: u32; + let mut k: usize; + let mut m: u64; + let mut t: Share; + + m = 0x00000000ffffffff; + j = 32; + while j != 0 { + k = 0; + while k < 64 { + t = ((&a[k] >> j) ^ &a[k + j as usize]) & m; + a[k + j as usize] ^= &t; + a[k] ^= t << j; + k = (k + j as usize + 1) & !(j as usize); + } + j >>= 1; + m = m ^ (m << j); + } + } + + pub fn transpose_pack_u64(self) -> Vec> { + self.transpose_pack_u64_with_len::<{ u64::BITS as usize }>() + } + + pub fn transpose_pack_u64_with_len(mut self) -> Vec> { + // Pad to multiple of 64 + let len = (self.shares.len() + 63) / 64; + self.shares.resize(len * 64, Share::default()); + + let mut res = (0..L) + .map(|_| VecShare::new_vec(vec![Share::default(); len])) + .collect::>(); + + for (j, x) in self.shares.chunks_exact_mut(64).enumerate() { + Self::share_transpose64x64(x.try_into().unwrap()); + for (src, des) in x.iter().cloned().zip(res.iter_mut()) { + des.shares[j] = src; + } + } + debug_assert_eq!(res.len(), L); + res + } + + pub fn transpose_pack_u128(self) -> Vec> { + self.transpose_pack_u128_with_len::<{ u64::BITS as usize }>() + } + + pub fn transpose_pack_u128_with_len(mut self) -> Vec> { + // Pad to multiple of 128 + let len = (self.shares.len() + 127) / 128; + self.shares.resize(len * 128, Share::default()); + + let mut res = (0..L) + .map(|_| VecShare::new_vec(vec![Share::default(); len])) + .collect::>(); + + for (j, x) in self.shares.chunks_exact(128).enumerate() { + let trans = Self::share_transpose64x128(x.try_into().unwrap()); + for (src, des) in trans.into_iter().zip(res.iter_mut()) { + des.shares[j] = src; + } + } + debug_assert_eq!(res.len(), L); + res + } +} diff --git a/iris-mpc-cpu/src/utils.rs b/iris-mpc-cpu/src/utils.rs new file mode 100644 index 000000000..2259f5493 --- /dev/null +++ b/iris-mpc-cpu/src/utils.rs @@ -0,0 +1,88 @@ +use crate::{ + networks::network_trait::NetworkTrait, + shares::{int_ring::IntRing2k, ring_impl::RingElement, vecshare::RingBytesIter}, +}; +use bytes::{Buf, BufMut, Bytes, BytesMut}; +use eyre::{eyre, Error, Result}; +use std::io; + +pub struct Utils {} + +impl Utils { + pub fn ring_slice_to_bytes(vec: &[RingElement]) -> Bytes { + let slice = RingElement::convert_slice(vec); + let slice_: &[u8] = bytemuck::cast_slice(slice); + Bytes::copy_from_slice(slice_) + } + + pub fn ring_iter_from_bytes( + bytes: BytesMut, + n: usize, + ) -> Result, Error> { + if bytes.remaining() != n * T::BYTES { + return Err(eyre!("InvalidSize")); + } + + Ok(RingBytesIter::new(bytes)) + } + + pub fn ring_to_bytes(value: &RingElement) -> Bytes { + let mut out = BytesMut::with_capacity(T::BYTES); + out.put(bytemuck::bytes_of(&value.0)); + out.freeze() + } + + pub fn ring_from_bytes(value: BytesMut) -> Result, Error> { + if value.remaining() != T::BYTES { + return Err(eyre!("InvalidSize")); + } + let slice = value.as_ref(); + let slice_: &T = bytemuck::from_bytes(slice); + Ok(RingElement(*slice_)) + } + + pub fn ring_iter_to_bytes<'a, T: 'a + IntRing2k>( + iter: impl ExactSizeIterator>, + ) -> Bytes { + let mut out = BytesMut::with_capacity(T::BYTES * iter.len()); + for v in iter { + out.put(bytemuck::bytes_of(&v.0)); + } + out.freeze() + } + + pub fn blocking_send_and_receive( + network: &mut N, + data: Bytes, + ) -> Result { + network.blocking_send_next_id(data)?; + let data = network.blocking_receive_prev_id()?; + Ok(data) + } + + pub fn blocking_send_slice_and_receive( + network: &mut N, + values: &[RingElement], + ) -> Result { + let data = Self::ring_slice_to_bytes(values); + Ok(Self::blocking_send_and_receive(network, data)?) + } + + pub fn blocking_send_and_receive_value( + network: &mut N, + value: &RingElement, + ) -> Result, Error> { + let response = Self::blocking_send_and_receive(network, Self::ring_to_bytes(value))?; + Self::ring_from_bytes(response) + } + + pub fn blocking_send_iter_and_receive<'a, N: NetworkTrait, T: IntRing2k + 'a>( + network: &mut N, + values: impl ExactSizeIterator>, + ) -> Result { + Ok(Self::blocking_send_and_receive( + network, + Self::ring_iter_to_bytes(values), + )?) + } +}