From 00c042d5a8654880a5ae12259b197a503502b0a4 Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Fri, 19 Jan 2024 00:57:19 +0100 Subject: [PATCH 01/13] Initialize `okamoto-uchiyama-cli` (cli tool) --- .gitignore | 18 +- Cargo.lock | 377 ------------------ README.md | 2 +- okamoto-uchiyama-cli/Cargo.toml | 8 + okamoto-uchiyama-cli/src/main.rs | 3 + Cargo.toml => okamoto-uchiyama-rs/Cargo.toml | 0 .../src}/crypto/mod.rs | 0 .../src}/crypto/okamoto_uchiyama.rs | 0 .../src}/crypto/private_key.rs | 0 .../src}/crypto/public_key.rs | 0 {src => okamoto-uchiyama-rs/src}/error.rs | 0 {src => okamoto-uchiyama-rs/src}/key.rs | 0 {src => okamoto-uchiyama-rs/src}/lib.rs | 0 {tests => okamoto-uchiyama-rs/tests}/tests.rs | 0 14 files changed, 29 insertions(+), 379 deletions(-) delete mode 100644 Cargo.lock create mode 100644 okamoto-uchiyama-cli/Cargo.toml create mode 100644 okamoto-uchiyama-cli/src/main.rs rename Cargo.toml => okamoto-uchiyama-rs/Cargo.toml (100%) rename {src => okamoto-uchiyama-rs/src}/crypto/mod.rs (100%) rename {src => okamoto-uchiyama-rs/src}/crypto/okamoto_uchiyama.rs (100%) rename {src => okamoto-uchiyama-rs/src}/crypto/private_key.rs (100%) rename {src => okamoto-uchiyama-rs/src}/crypto/public_key.rs (100%) rename {src => okamoto-uchiyama-rs/src}/error.rs (100%) rename {src => okamoto-uchiyama-rs/src}/key.rs (100%) rename {src => okamoto-uchiyama-rs/src}/lib.rs (100%) rename {tests => okamoto-uchiyama-rs/tests}/tests.rs (100%) diff --git a/.gitignore b/.gitignore index ea8c4bf..4ba2159 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,17 @@ -/target +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# .rlib files +**/*.rlib + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index e724618..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,377 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin", -] - -[[package]] -name = "libc" -version = "0.2.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" - -[[package]] -name = "libm" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "num" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" -dependencies = [ - "num-bigint 0.4.3", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", - "rand 0.5.6", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2399c9463abc5f909349d8aa9ba080e0b88b3ce2885389b60b993f39b1a56905" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "serde", - "smallvec", -] - -[[package]] -name = "num-complex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-primes" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f25459a616100b36b5af31d8b05abbee29a5f29f83ddf95e78cb2268ab300a" -dependencies = [ - "log", - "num", - "num-bigint 0.2.6", - "num-traits", - "rand 0.5.6", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-bigint 0.4.3", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "okamoto-uchiyama" -version = "0.1.0" -dependencies = [ - "num", - "num-bigint-dig", - "num-primes", - "rand 0.8.5", - "thiserror", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "1.0.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "winapi", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "serde" -version = "1.0.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "syn" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "unicode-ident" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/README.md b/README.md index c3449f9..376105a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# okamoto-uchiyama-rust +# okamoto-uchiyama-rs The Okamoto–Uchiyama cryptosystem is a public key cryptosystem proposed in 1998 by Tatsuaki Okamoto and Shigenori Uchiyama. The scheme is an additive homomorphic cryptosystem; this means that, given only the public key and the encryption of **`m_1`** and **`m_2`**, one can compute the encryption of **`m_1 + m_2`**. diff --git a/okamoto-uchiyama-cli/Cargo.toml b/okamoto-uchiyama-cli/Cargo.toml new file mode 100644 index 0000000..659d3f4 --- /dev/null +++ b/okamoto-uchiyama-cli/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "okmoto-uchiyama-cli" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/okamoto-uchiyama-cli/src/main.rs b/okamoto-uchiyama-cli/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/okamoto-uchiyama-cli/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/Cargo.toml b/okamoto-uchiyama-rs/Cargo.toml similarity index 100% rename from Cargo.toml rename to okamoto-uchiyama-rs/Cargo.toml diff --git a/src/crypto/mod.rs b/okamoto-uchiyama-rs/src/crypto/mod.rs similarity index 100% rename from src/crypto/mod.rs rename to okamoto-uchiyama-rs/src/crypto/mod.rs diff --git a/src/crypto/okamoto_uchiyama.rs b/okamoto-uchiyama-rs/src/crypto/okamoto_uchiyama.rs similarity index 100% rename from src/crypto/okamoto_uchiyama.rs rename to okamoto-uchiyama-rs/src/crypto/okamoto_uchiyama.rs diff --git a/src/crypto/private_key.rs b/okamoto-uchiyama-rs/src/crypto/private_key.rs similarity index 100% rename from src/crypto/private_key.rs rename to okamoto-uchiyama-rs/src/crypto/private_key.rs diff --git a/src/crypto/public_key.rs b/okamoto-uchiyama-rs/src/crypto/public_key.rs similarity index 100% rename from src/crypto/public_key.rs rename to okamoto-uchiyama-rs/src/crypto/public_key.rs diff --git a/src/error.rs b/okamoto-uchiyama-rs/src/error.rs similarity index 100% rename from src/error.rs rename to okamoto-uchiyama-rs/src/error.rs diff --git a/src/key.rs b/okamoto-uchiyama-rs/src/key.rs similarity index 100% rename from src/key.rs rename to okamoto-uchiyama-rs/src/key.rs diff --git a/src/lib.rs b/okamoto-uchiyama-rs/src/lib.rs similarity index 100% rename from src/lib.rs rename to okamoto-uchiyama-rs/src/lib.rs diff --git a/tests/tests.rs b/okamoto-uchiyama-rs/tests/tests.rs similarity index 100% rename from tests/tests.rs rename to okamoto-uchiyama-rs/tests/tests.rs From c4826040a99d0602cfc490041b1e3da8fdca6136 Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Sat, 20 Jan 2024 01:14:16 +0100 Subject: [PATCH 02/13] Implement an example of key pair generation --- .../examples/key_generation.rs | 22 +++++++++++++++++++ okamoto-uchiyama-rs/src/crypto/private_key.rs | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 okamoto-uchiyama-rs/examples/key_generation.rs diff --git a/okamoto-uchiyama-rs/examples/key_generation.rs b/okamoto-uchiyama-rs/examples/key_generation.rs new file mode 100644 index 0000000..c4b126a --- /dev/null +++ b/okamoto-uchiyama-rs/examples/key_generation.rs @@ -0,0 +1,22 @@ +use num_bigint_dig::BigUint; +use okamoto_uchiyama::{PrivateKey, PublicKey}; + +fn main() { + // Creating a public key with three large integers as parameters + let public_key = PublicKey::new( + &BigUint::from(9432233159u64), + &BigUint::from(8083706871u64), + &BigUint::from(7988052977u64), + ); + + // Creating a private key with the corresponding public key and two additional parameters + let private_key = PrivateKey::new( + &public_key, + &BigUint::from(2003u64), + &BigUint::from(2351u64), + ); + + // Printing the public and private keys + println!("{:#?}", public_key); + println!("{:#?}", private_key); +} \ No newline at end of file diff --git a/okamoto-uchiyama-rs/src/crypto/private_key.rs b/okamoto-uchiyama-rs/src/crypto/private_key.rs index 4cb67ec..7516da7 100644 --- a/okamoto-uchiyama-rs/src/crypto/private_key.rs +++ b/okamoto-uchiyama-rs/src/crypto/private_key.rs @@ -28,7 +28,7 @@ impl PrivateKey { let p_squared = &p * &p; // Generate gd let gd = public_key.g.modpow(&(&p - &1u32), &p_squared) % &p_squared; - println!("{}", gd); + PrivateKey { public_key, gd, From f0b0b8f6b187ecd29581338a1fa1c6894ce6a3d6 Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Sat, 20 Jan 2024 01:13:53 +0100 Subject: [PATCH 03/13] Implement `fmt::Display` for PublicKey and PrivateKey --- okamoto-uchiyama-rs/examples/key_generation.rs | 6 +++--- okamoto-uchiyama-rs/src/crypto/private_key.rs | 18 ++++++++++++++++++ okamoto-uchiyama-rs/src/crypto/public_key.rs | 12 ++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/okamoto-uchiyama-rs/examples/key_generation.rs b/okamoto-uchiyama-rs/examples/key_generation.rs index c4b126a..4ba5f49 100644 --- a/okamoto-uchiyama-rs/examples/key_generation.rs +++ b/okamoto-uchiyama-rs/examples/key_generation.rs @@ -17,6 +17,6 @@ fn main() { ); // Printing the public and private keys - println!("{:#?}", public_key); - println!("{:#?}", private_key); -} \ No newline at end of file + println!("{}", public_key); + println!("{}", private_key); +} diff --git a/okamoto-uchiyama-rs/src/crypto/private_key.rs b/okamoto-uchiyama-rs/src/crypto/private_key.rs index 7516da7..6255996 100644 --- a/okamoto-uchiyama-rs/src/crypto/private_key.rs +++ b/okamoto-uchiyama-rs/src/crypto/private_key.rs @@ -1,6 +1,7 @@ use crate::crypto::okamoto_uchiyama::PublicKey; use num_bigint_dig::BigUint; +use std::fmt; /// PrivateKey represents a Okamoto-Uchiyama private key. #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] @@ -38,3 +39,20 @@ impl PrivateKey { } } } + +// Implementation of the Display trait for the PrivateKey struct +impl fmt::Display for PrivateKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "PrivateKey {{ + public_key: {}, + gd: {}, + p: {}, + q: {}, + p_squared: {} +}}", + self.public_key, self.gd, self.p, self.q, self.p_squared + ) + } +} diff --git a/okamoto-uchiyama-rs/src/crypto/public_key.rs b/okamoto-uchiyama-rs/src/crypto/public_key.rs index 46d21bf..cbe5bd3 100644 --- a/okamoto-uchiyama-rs/src/crypto/public_key.rs +++ b/okamoto-uchiyama-rs/src/crypto/public_key.rs @@ -4,6 +4,7 @@ use num::One; use num_bigint_dig::BigUint; pub use crate::crypto::private_key::PrivateKey; +use std::fmt; /// Represents a Okamoto-Uchiyama public key. #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] @@ -62,3 +63,14 @@ impl PublicKey { Ok(c) } } + +// Implementation of the Display trait for the PublicKey struct +impl fmt::Display for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "PublicKey {{\n n: {},\n g: {},\n h: {}\n}}", + self.n, self.g, self.h + ) + } +} From 2822f0fe7638b387e8305b3988baaec752ae24ae Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Sun, 21 Jan 2024 02:18:07 +0100 Subject: [PATCH 04/13] Implement PEM Encoding for Public and Private Key --- okamoto-uchiyama-rs/Cargo.toml | 1 + .../examples/key_generation_pem.rs | 23 ++++++++++++++++ okamoto-uchiyama-rs/src/crypto/private_key.rs | 25 ++++++++++++++++- okamoto-uchiyama-rs/src/crypto/public_key.rs | 27 +++++++++++++++++-- okamoto-uchiyama-rs/src/lib.rs | 1 + okamoto-uchiyama-rs/src/pem.rs | 5 ++++ 6 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 okamoto-uchiyama-rs/examples/key_generation_pem.rs create mode 100644 okamoto-uchiyama-rs/src/pem.rs diff --git a/okamoto-uchiyama-rs/Cargo.toml b/okamoto-uchiyama-rs/Cargo.toml index 4d1daa7..c181361 100644 --- a/okamoto-uchiyama-rs/Cargo.toml +++ b/okamoto-uchiyama-rs/Cargo.toml @@ -12,6 +12,7 @@ keywords = ["okamoto", "uchiyama", "cryptography", "homomorphic"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +base64 = "0.21.7" num = "0.4.0" num-bigint-dig = { version = "0.8.2" } num-primes = "0.3.0" diff --git a/okamoto-uchiyama-rs/examples/key_generation_pem.rs b/okamoto-uchiyama-rs/examples/key_generation_pem.rs new file mode 100644 index 0000000..833afd5 --- /dev/null +++ b/okamoto-uchiyama-rs/examples/key_generation_pem.rs @@ -0,0 +1,23 @@ +use num_bigint_dig::BigUint; +use okamoto_uchiyama::pem::PemEncodable; +use okamoto_uchiyama::{PrivateKey, PublicKey}; + +fn main() { + // Creating a public key with three large integers as parameters + let public_key = PublicKey::new( + &BigUint::from(9432233159u64), + &BigUint::from(8083706871u64), + &BigUint::from(7988052977u64), + ); + + // Creating a private key with the corresponding public key and two additional parameters + let private_key = PrivateKey::new( + &public_key, + &BigUint::from(2003u64), + &BigUint::from(2351u64), + ); + + // Printing the public and private keys + println!("{}", public_key.to_pem()); + println!("{}", private_key.to_pem()); +} diff --git a/okamoto-uchiyama-rs/src/crypto/private_key.rs b/okamoto-uchiyama-rs/src/crypto/private_key.rs index 6255996..31b63ce 100644 --- a/okamoto-uchiyama-rs/src/crypto/private_key.rs +++ b/okamoto-uchiyama-rs/src/crypto/private_key.rs @@ -1,5 +1,6 @@ use crate::crypto::okamoto_uchiyama::PublicKey; - +use crate::pem::PemEncodable; +use base64::{engine::general_purpose, Engine as _}; use num_bigint_dig::BigUint; use std::fmt; @@ -38,6 +39,17 @@ impl PrivateKey { p_squared, } } + + /// Convert the private key to DER format + pub fn to_der(&self) -> Vec { + let mut der = Vec::new(); + der.extend_from_slice(&self.public_key.to_der()); + der.extend_from_slice(&self.gd.to_bytes_be()); + der.extend_from_slice(&self.p.to_bytes_be()); + der.extend_from_slice(&self.q.to_bytes_be()); + der.extend_from_slice(&self.p_squared.to_bytes_be()); + der + } } // Implementation of the Display trait for the PrivateKey struct @@ -56,3 +68,14 @@ impl fmt::Display for PrivateKey { ) } } + +/// Implements the PemEncodable trait for PrivateKey struct +impl PemEncodable for PrivateKey { + fn to_pem(&self) -> String { + let mut pem = String::new(); + pem.push_str("-----BEGIN PRIVATE KEY-----\n"); + pem.push_str(&general_purpose::STANDARD.encode(&self.to_der())); + pem.push_str("\n-----END PRIVATE KEY-----\n"); + pem + } +} diff --git a/okamoto-uchiyama-rs/src/crypto/public_key.rs b/okamoto-uchiyama-rs/src/crypto/public_key.rs index cbe5bd3..2e887f9 100644 --- a/okamoto-uchiyama-rs/src/crypto/public_key.rs +++ b/okamoto-uchiyama-rs/src/crypto/public_key.rs @@ -1,10 +1,13 @@ use crate::error::OkamotoUchiyamaError; +use crate::pem::PemEncodable; +use base64::engine::general_purpose; +use base64::Engine; use num::One; use num_bigint_dig::BigUint; +use std::fmt; pub use crate::crypto::private_key::PrivateKey; -use std::fmt; /// Represents a Okamoto-Uchiyama public key. #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] @@ -62,9 +65,18 @@ impl PublicKey { } Ok(c) } + + /// Convert the public key to DER format + pub fn to_der(&self) -> Vec { + let mut der = Vec::new(); + der.extend_from_slice(&self.n.to_bytes_be()); + der.extend_from_slice(&self.g.to_bytes_be()); + der.extend_from_slice(&self.h.to_bytes_be()); + der + } } -// Implementation of the Display trait for the PublicKey struct +// Implements Display trait for the PublicKey struct impl fmt::Display for PublicKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -74,3 +86,14 @@ impl fmt::Display for PublicKey { ) } } + +/// Implements the PemEncodable trait from PublicKey struct +impl PemEncodable for PublicKey { + fn to_pem(&self) -> String { + let mut pem = String::new(); + pem.push_str("-----BEGIN PUBLIC KEY-----\n"); + pem.push_str(&general_purpose::STANDARD.encode(&self.to_der())); + pem.push_str("\n-----END PUBLIC KEY-----\n"); + pem + } +} diff --git a/okamoto-uchiyama-rs/src/lib.rs b/okamoto-uchiyama-rs/src/lib.rs index 0ab7ff6..f556413 100644 --- a/okamoto-uchiyama-rs/src/lib.rs +++ b/okamoto-uchiyama-rs/src/lib.rs @@ -11,6 +11,7 @@ pub mod crypto; pub mod error; pub mod key; +pub mod pem; // Re-exporting types from the 'crypto' module for external use pub use crypto::okamoto_uchiyama::{OkamotoUchiyama, PrivateKey, PublicKey}; diff --git a/okamoto-uchiyama-rs/src/pem.rs b/okamoto-uchiyama-rs/src/pem.rs new file mode 100644 index 0000000..44becab --- /dev/null +++ b/okamoto-uchiyama-rs/src/pem.rs @@ -0,0 +1,5 @@ +/// A trait for types that can be encoded into PEM (Privacy Enhanced Mail) format. +pub trait PemEncodable { + /// Converts the implementor into a PEM-encoded string + fn to_pem(&self) -> String; +} From 0d7e981ca1660020aa01175a31acfaff4ce35bd5 Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Sun, 21 Jan 2024 02:31:05 +0100 Subject: [PATCH 05/13] Refactor unit tests --- okamoto-uchiyama-rs/tests/test_decryption.rs | 41 +++++++++++++++ .../tests/{tests.rs => test_encryption.rs} | 41 +-------------- okamoto-uchiyama-rs/tests/test_pem.rs | 50 +++++++++++++++++++ 3 files changed, 92 insertions(+), 40 deletions(-) create mode 100644 okamoto-uchiyama-rs/tests/test_decryption.rs rename okamoto-uchiyama-rs/tests/{tests.rs => test_encryption.rs} (56%) create mode 100644 okamoto-uchiyama-rs/tests/test_pem.rs diff --git a/okamoto-uchiyama-rs/tests/test_decryption.rs b/okamoto-uchiyama-rs/tests/test_decryption.rs new file mode 100644 index 0000000..ec90a5b --- /dev/null +++ b/okamoto-uchiyama-rs/tests/test_decryption.rs @@ -0,0 +1,41 @@ +use num_bigint_dig::BigUint; +use okamoto_uchiyama::{OkamotoUchiyama, PrivateKey, PublicKey}; + +#[test] +fn test_encryption_decryption() { + let message = BigUint::from(1337u64); + + // Initialization + let length = okamoto_uchiyama::key::KeySize::Bits1024; + let okamoto_uchiyama = OkamotoUchiyama::init(length); + + // Generate the key pair + let private_key = okamoto_uchiyama.generate_private_key(); + let public_key = private_key.public_key.clone(); + + let ciphertext = OkamotoUchiyama::encrypt(&message, &public_key); + let plaintext: BigUint = OkamotoUchiyama::decrypt(&ciphertext, &private_key); + + assert_eq!(message, plaintext); +} + +#[test] +fn test_encryption_decryption_from_public_key() { + let message = BigUint::from(1337u64); + + let public_key = PublicKey::new( + &BigUint::from(9432233159u64), + &BigUint::from(8083706871u64), + &BigUint::from(7988052977u64), + ); + let private_key = PrivateKey::new( + &public_key, + &BigUint::from(2003u64), + &BigUint::from(2351u64), + ); + + let ciphertext = OkamotoUchiyama::encrypt(&message, &public_key); + let plaintext: BigUint = OkamotoUchiyama::decrypt(&ciphertext, &private_key); + + assert_eq!(message, plaintext); +} diff --git a/okamoto-uchiyama-rs/tests/tests.rs b/okamoto-uchiyama-rs/tests/test_encryption.rs similarity index 56% rename from okamoto-uchiyama-rs/tests/tests.rs rename to okamoto-uchiyama-rs/tests/test_encryption.rs index 5283770..141136e 100644 --- a/okamoto-uchiyama-rs/tests/tests.rs +++ b/okamoto-uchiyama-rs/tests/test_encryption.rs @@ -1,44 +1,5 @@ use num_bigint_dig::BigUint; -use okamoto_uchiyama::{OkamotoUchiyama, PrivateKey, PublicKey}; - -#[test] -fn test_encryption_decryption() { - let message = BigUint::from(1337u64); - - // Initialization - let length = okamoto_uchiyama::key::KeySize::Bits1024; - let okamoto_uchiyama = OkamotoUchiyama::init(length); - - // Generate the key pair - let private_key = okamoto_uchiyama.generate_private_key(); - let public_key = private_key.public_key.clone(); - - let ciphertext = OkamotoUchiyama::encrypt(&message, &public_key); - let plaintext: BigUint = OkamotoUchiyama::decrypt(&ciphertext, &private_key); - - assert_eq!(message, plaintext); -} - -#[test] -fn test_encryption_decryption_from_public_key() { - let message = BigUint::from(1337u64); - - let public_key = PublicKey::new( - &BigUint::from(9432233159u64), - &BigUint::from(8083706871u64), - &BigUint::from(7988052977u64), - ); - let private_key = PrivateKey::new( - &public_key, - &BigUint::from(2003u64), - &BigUint::from(2351u64), - ); - - let ciphertext = OkamotoUchiyama::encrypt(&message, &public_key); - let plaintext: BigUint = OkamotoUchiyama::decrypt(&ciphertext, &private_key); - - assert_eq!(message, plaintext); -} +use okamoto_uchiyama::OkamotoUchiyama; #[test] fn test_homomorphic_encrypt_two() { diff --git a/okamoto-uchiyama-rs/tests/test_pem.rs b/okamoto-uchiyama-rs/tests/test_pem.rs new file mode 100644 index 0000000..7aaa75d --- /dev/null +++ b/okamoto-uchiyama-rs/tests/test_pem.rs @@ -0,0 +1,50 @@ +use num_bigint_dig::BigUint; +use okamoto_uchiyama::pem::PemEncodable; +use okamoto_uchiyama::{PrivateKey, PublicKey}; + +#[test] +fn test_public_key_pem_encoding() { + // Create a sample public key + let public_key = PublicKey::new( + &BigUint::from(9432233159u64), + &BigUint::from(8083706871u64), + &BigUint::from(7988052977u64), + ); + + // Encode to PEM + let pem_str = public_key.to_pem(); + + // Expected PEM-encoded string + let expected_pem = "-----BEGIN PUBLIC KEY-----\n\ + AjI0dMcB4dOT9wHcIAPx\n\ + -----END PUBLIC KEY-----\n"; + + // Assert equality + assert_eq!(pem_str, expected_pem); +} + +#[test] +fn test_private_key_pem_encoding() { + // Create a sample private key + let public_key = PublicKey::new( + &BigUint::from(9432233159u64), + &BigUint::from(8083706871u64), + &BigUint::from(7988052977u64), + ); + let private_key = PrivateKey::new( + &public_key, + &BigUint::from(2003u64), + &BigUint::from(2351u64), + ); + + // Encode to PEM + let pem_str = private_key.to_pem(); + + // Expected PEM-encoded string + let expected_pem = "-----BEGIN PRIVATE KEY-----\n\ + AjI0dMcB4dOT9wHcIAPxH2N6B9MJLz036Q==\n\ + -----END PRIVATE KEY-----\n"; + + // Assert equality + assert_eq!(pem_str, expected_pem); +} From eb0caa3492c639d76870065a64b6ae4a6d2ed3c0 Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Wed, 24 Jan 2024 17:25:40 +0100 Subject: [PATCH 06/13] Enhance ASN.1 DER Encoding for BigUint --- okamoto-uchiyama-rs/src/crypto/private_key.rs | 16 ++++---- okamoto-uchiyama-rs/src/crypto/public_key.rs | 20 +++++----- okamoto-uchiyama-rs/src/pem.rs | 37 +++++++++++++++++++ okamoto-uchiyama-rs/tests/test_pem.rs | 4 +- 4 files changed, 57 insertions(+), 20 deletions(-) diff --git a/okamoto-uchiyama-rs/src/crypto/private_key.rs b/okamoto-uchiyama-rs/src/crypto/private_key.rs index 31b63ce..77beef9 100644 --- a/okamoto-uchiyama-rs/src/crypto/private_key.rs +++ b/okamoto-uchiyama-rs/src/crypto/private_key.rs @@ -1,10 +1,10 @@ use crate::crypto::okamoto_uchiyama::PublicKey; -use crate::pem::PemEncodable; +use crate::pem::{Asn1Encode, PemEncodable}; use base64::{engine::general_purpose, Engine as _}; use num_bigint_dig::BigUint; use std::fmt; -/// PrivateKey represents a Okamoto-Uchiyama private key. +/// PrivateKey represents an Okamoto-Uchiyama private key. #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] pub struct PrivateKey { // The public key corresponding to this private key @@ -20,7 +20,7 @@ pub struct PrivateKey { } impl PrivateKey { - /// Generate a new private key from p, q and a public key + /// Generate a new private key from p, q, and a public key pub fn new(public_key: &PublicKey, p: &BigUint, q: &BigUint) -> PrivateKey { let public_key = public_key.clone(); let p = p.clone(); @@ -40,14 +40,14 @@ impl PrivateKey { } } - /// Convert the private key to DER format + /// Convert the private key to ASN.1 DER format pub fn to_der(&self) -> Vec { let mut der = Vec::new(); der.extend_from_slice(&self.public_key.to_der()); - der.extend_from_slice(&self.gd.to_bytes_be()); - der.extend_from_slice(&self.p.to_bytes_be()); - der.extend_from_slice(&self.q.to_bytes_be()); - der.extend_from_slice(&self.p_squared.to_bytes_be()); + der.extend_from_slice(&self.gd.to_asn1_der()); + der.extend_from_slice(&self.p.to_asn1_der()); + der.extend_from_slice(&self.q.to_asn1_der()); + der.extend_from_slice(&self.p_squared.to_asn1_der()); der } } diff --git a/okamoto-uchiyama-rs/src/crypto/public_key.rs b/okamoto-uchiyama-rs/src/crypto/public_key.rs index 2e887f9..bcabdab 100644 --- a/okamoto-uchiyama-rs/src/crypto/public_key.rs +++ b/okamoto-uchiyama-rs/src/crypto/public_key.rs @@ -1,5 +1,5 @@ use crate::error::OkamotoUchiyamaError; -use crate::pem::PemEncodable; +use crate::pem::{Asn1Encode, PemEncodable}; use base64::engine::general_purpose; use base64::Engine; @@ -9,7 +9,7 @@ use std::fmt; pub use crate::crypto::private_key::PrivateKey; -/// Represents a Okamoto-Uchiyama public key. +/// Represents an Okamoto-Uchiyama public key. #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] pub struct PublicKey { // modulus: p^2 * q @@ -21,7 +21,7 @@ pub struct PublicKey { } impl PublicKey { - /// Generate a public key from n, g and h + /// Generate a public key from n, g, and h pub fn new(n: &BigUint, g: &BigUint, h: &BigUint) -> PublicKey { PublicKey { n: n.clone(), @@ -30,7 +30,7 @@ impl PublicKey { } } - /// Performs homomorphic operation over two passed chiphers. + /// Performs homomorphic operation over two passed ciphers. /// Okamoto-Uchiyama has additive homomorphic property, so resultant cipher /// contains the sum of two numbers. pub fn homomorphic_encrypt_two( @@ -46,7 +46,7 @@ impl PublicKey { Ok((c1 * c2) % &self.n) } - /// Performs homomorphic operation over multiple passed chiphers. + /// Performs homomorphic operation over multiple passed ciphers. /// Okamoto-Uchiyama has additive homomorphic property, so resultant cipher /// contains the sum of multiple numbers. pub fn homomorphic_encrypt_multiple( @@ -66,12 +66,12 @@ impl PublicKey { Ok(c) } - /// Convert the public key to DER format + /// Convert the public key to ASN.1 DER format pub fn to_der(&self) -> Vec { let mut der = Vec::new(); - der.extend_from_slice(&self.n.to_bytes_be()); - der.extend_from_slice(&self.g.to_bytes_be()); - der.extend_from_slice(&self.h.to_bytes_be()); + der.extend_from_slice(&self.n.to_asn1_der()); + der.extend_from_slice(&self.g.to_asn1_der()); + der.extend_from_slice(&self.h.to_asn1_der()); der } } @@ -87,7 +87,7 @@ impl fmt::Display for PublicKey { } } -/// Implements the PemEncodable trait from PublicKey struct +/// Implements the PemEncodable trait for PublicKey struct impl PemEncodable for PublicKey { fn to_pem(&self) -> String { let mut pem = String::new(); diff --git a/okamoto-uchiyama-rs/src/pem.rs b/okamoto-uchiyama-rs/src/pem.rs index 44becab..59072e1 100644 --- a/okamoto-uchiyama-rs/src/pem.rs +++ b/okamoto-uchiyama-rs/src/pem.rs @@ -1,5 +1,42 @@ +use num_bigint_dig::BigUint; + /// A trait for types that can be encoded into PEM (Privacy Enhanced Mail) format. pub trait PemEncodable { /// Converts the implementor into a PEM-encoded string fn to_pem(&self) -> String; } + +/// A trait for types that can be encoded in ASN.1 DER (Distinguished Encoding Rules) format. +pub trait Asn1Encode { + /// Converts the implementor into an ASN.1 DER-encoded byte vector. + fn to_asn1_der(&self) -> Vec; +} + +/// Implementation of the Asn1Encode trait for the BigUint type. +impl Asn1Encode for BigUint { + /// Converts a BigUint into an ASN.1 DER-encoded byte vector. + fn to_asn1_der(&self) -> Vec { + // Create a new vector to store the ASN.1 DER encoding + let mut der = Vec::new(); + + // Encode Type + der.push(0x02); + + // Encode Length + let value_bytes = self.to_bytes_be(); + let length_byte_count = value_bytes + .iter() + .position(|&b| b != 0) + .map_or(0, |pos| value_bytes.len() - pos); + + // Always use long form for the length representation + let length_bytes = length_byte_count.to_be_bytes(); + der.push((0x80 | length_bytes.len()) as u8); // Set the most significant bit to indicate long form + der.extend_from_slice(&length_bytes); + + // Encode Value + der.extend_from_slice(&value_bytes); + + der + } +} diff --git a/okamoto-uchiyama-rs/tests/test_pem.rs b/okamoto-uchiyama-rs/tests/test_pem.rs index 7aaa75d..884b848 100644 --- a/okamoto-uchiyama-rs/tests/test_pem.rs +++ b/okamoto-uchiyama-rs/tests/test_pem.rs @@ -16,7 +16,7 @@ fn test_public_key_pem_encoding() { // Expected PEM-encoded string let expected_pem = "-----BEGIN PUBLIC KEY-----\n\ - AjI0dMcB4dOT9wHcIAPx\n\ + AgUCMjR0xwIFAeHTk/cCBQHcIAPx\n\ -----END PUBLIC KEY-----\n"; // Assert equality @@ -42,7 +42,7 @@ fn test_private_key_pem_encoding() { // Expected PEM-encoded string let expected_pem = "-----BEGIN PRIVATE KEY-----\n\ - AjI0dMcB4dOT9wHcIAPxH2N6B9MJLz036Q==\n\ + AgUCMjR0xwIFAeHTk/cCBQHcIAPxAgMfY3oCAgfTAgIJLwIDPTfp\n\ -----END PRIVATE KEY-----\n"; // Assert equality From 5b03fa682c7490d533804335c042d60a8ae0e323 Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Fri, 9 Feb 2024 01:51:26 +0100 Subject: [PATCH 07/13] Encode public and private keys using ASN.1 serialization --- okamoto-uchiyama-rs/Cargo.toml | 3 +- okamoto-uchiyama-rs/src/crypto/private_key.rs | 56 ++++++++++++++----- okamoto-uchiyama-rs/src/crypto/public_key.rs | 37 ++++++++---- okamoto-uchiyama-rs/src/pem.rs | 37 ------------ okamoto-uchiyama-rs/tests/test_pem.rs | 4 +- 5 files changed, 73 insertions(+), 64 deletions(-) diff --git a/okamoto-uchiyama-rs/Cargo.toml b/okamoto-uchiyama-rs/Cargo.toml index c181361..0133034 100644 --- a/okamoto-uchiyama-rs/Cargo.toml +++ b/okamoto-uchiyama-rs/Cargo.toml @@ -12,9 +12,10 @@ keywords = ["okamoto", "uchiyama", "cryptography", "homomorphic"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +asn1 = "0.16.0" base64 = "0.21.7" num = "0.4.0" -num-bigint-dig = { version = "0.8.2" } +num-bigint-dig = "0.8.2" num-primes = "0.3.0" rand = "0.8.5" thiserror = "1.0" diff --git a/okamoto-uchiyama-rs/src/crypto/private_key.rs b/okamoto-uchiyama-rs/src/crypto/private_key.rs index 77beef9..9fa13b1 100644 --- a/okamoto-uchiyama-rs/src/crypto/private_key.rs +++ b/okamoto-uchiyama-rs/src/crypto/private_key.rs @@ -1,5 +1,6 @@ use crate::crypto::okamoto_uchiyama::PublicKey; -use crate::pem::{Asn1Encode, PemEncodable}; +use crate::pem::PemEncodable; +use asn1::BigUint as Asn1BigUint; use base64::{engine::general_purpose, Engine as _}; use num_bigint_dig::BigUint; use std::fmt; @@ -39,17 +40,6 @@ impl PrivateKey { p_squared, } } - - /// Convert the private key to ASN.1 DER format - pub fn to_der(&self) -> Vec { - let mut der = Vec::new(); - der.extend_from_slice(&self.public_key.to_der()); - der.extend_from_slice(&self.gd.to_asn1_der()); - der.extend_from_slice(&self.p.to_asn1_der()); - der.extend_from_slice(&self.q.to_asn1_der()); - der.extend_from_slice(&self.p_squared.to_asn1_der()); - der - } } // Implementation of the Display trait for the PrivateKey struct @@ -73,9 +63,49 @@ impl fmt::Display for PrivateKey { impl PemEncodable for PrivateKey { fn to_pem(&self) -> String { let mut pem = String::new(); + + // Convert public key components to ASN.1 + let n_bytes = self.public_key.n.clone().to_bytes_be(); + let n_asn1 = Asn1BigUint::new(&n_bytes); + + let g_bytes = self.public_key.g.clone().to_bytes_be(); + let g_asn1 = Asn1BigUint::new(&g_bytes); + + let h_bytes = self.public_key.h.clone().to_bytes_be(); + let h_asn1 = Asn1BigUint::new(&h_bytes); + + // Convert private key components to ASN.1 + let gd_bytes = self.gd.clone().to_bytes_be(); + let gd_asn1 = Asn1BigUint::new(&gd_bytes); + + let p_bytes = self.p.clone().to_bytes_be(); + let p_asn1 = Asn1BigUint::new(&p_bytes); + + let q_bytes = self.q.clone().to_bytes_be(); + let q_asn1 = Asn1BigUint::new(&q_bytes); + + let p_squared_bytes = self.p_squared.clone().to_bytes_be(); + let p_squared_bytes_asn1 = Asn1BigUint::new(&p_squared_bytes); + + // Write all elements to ASN.1 Sequence + let result = asn1::write(|w| { + w.write_element(&asn1::SequenceWriter::new(&|w| { + w.write_element(&n_asn1)?; // Add n to the sequence + w.write_element(&g_asn1)?; // Add g to the sequence + w.write_element(&h_asn1)?; // Add h to the sequence + w.write_element(&gd_asn1)?; // Add gd to the sequence + w.write_element(&p_asn1)?; // Add p to the sequence + w.write_element(&q_asn1)?; // Add q to the sequence + w.write_element(&p_squared_bytes_asn1)?; // Add p_squared to the sequence + Ok(()) + })) + }); + + // Encode the ASN.1 sequence using Base64 pem.push_str("-----BEGIN PRIVATE KEY-----\n"); - pem.push_str(&general_purpose::STANDARD.encode(&self.to_der())); + pem.push_str(&general_purpose::STANDARD.encode(result.unwrap_or_else(|_| vec![]))); pem.push_str("\n-----END PRIVATE KEY-----\n"); + pem } } diff --git a/okamoto-uchiyama-rs/src/crypto/public_key.rs b/okamoto-uchiyama-rs/src/crypto/public_key.rs index bcabdab..a3c90a2 100644 --- a/okamoto-uchiyama-rs/src/crypto/public_key.rs +++ b/okamoto-uchiyama-rs/src/crypto/public_key.rs @@ -1,6 +1,7 @@ use crate::error::OkamotoUchiyamaError; -use crate::pem::{Asn1Encode, PemEncodable}; +use crate::pem::PemEncodable; +use asn1::BigUint as Asn1BigUint; use base64::engine::general_purpose; use base64::Engine; use num::One; @@ -65,15 +66,6 @@ impl PublicKey { } Ok(c) } - - /// Convert the public key to ASN.1 DER format - pub fn to_der(&self) -> Vec { - let mut der = Vec::new(); - der.extend_from_slice(&self.n.to_asn1_der()); - der.extend_from_slice(&self.g.to_asn1_der()); - der.extend_from_slice(&self.h.to_asn1_der()); - der - } } // Implements Display trait for the PublicKey struct @@ -91,9 +83,32 @@ impl fmt::Display for PublicKey { impl PemEncodable for PublicKey { fn to_pem(&self) -> String { let mut pem = String::new(); + + // Convert public key components to ASN.1 + let n_bytes = self.n.clone().to_bytes_be(); + let n_asn1 = Asn1BigUint::new(&n_bytes); + + let g_bytes = self.g.clone().to_bytes_be(); + let g_asn1 = Asn1BigUint::new(&g_bytes); + + let h_bytes = self.h.clone().to_bytes_be(); + let h_asn1 = Asn1BigUint::new(&h_bytes); + + // Write all elements to ASN.1 Sequence + let result = asn1::write(|w| { + w.write_element(&asn1::SequenceWriter::new(&|w| { + w.write_element(&n_asn1)?; // Add n to the sequence + w.write_element(&g_asn1)?; // Add g to the sequence + w.write_element(&h_asn1)?; // Add h to the sequence + Ok(()) + })) + }); + + // Encode the ASN.1 sequence using Base64 pem.push_str("-----BEGIN PUBLIC KEY-----\n"); - pem.push_str(&general_purpose::STANDARD.encode(&self.to_der())); + pem.push_str(&general_purpose::STANDARD.encode(result.unwrap_or_else(|_| vec![]))); pem.push_str("\n-----END PUBLIC KEY-----\n"); + pem } } diff --git a/okamoto-uchiyama-rs/src/pem.rs b/okamoto-uchiyama-rs/src/pem.rs index 59072e1..44becab 100644 --- a/okamoto-uchiyama-rs/src/pem.rs +++ b/okamoto-uchiyama-rs/src/pem.rs @@ -1,42 +1,5 @@ -use num_bigint_dig::BigUint; - /// A trait for types that can be encoded into PEM (Privacy Enhanced Mail) format. pub trait PemEncodable { /// Converts the implementor into a PEM-encoded string fn to_pem(&self) -> String; } - -/// A trait for types that can be encoded in ASN.1 DER (Distinguished Encoding Rules) format. -pub trait Asn1Encode { - /// Converts the implementor into an ASN.1 DER-encoded byte vector. - fn to_asn1_der(&self) -> Vec; -} - -/// Implementation of the Asn1Encode trait for the BigUint type. -impl Asn1Encode for BigUint { - /// Converts a BigUint into an ASN.1 DER-encoded byte vector. - fn to_asn1_der(&self) -> Vec { - // Create a new vector to store the ASN.1 DER encoding - let mut der = Vec::new(); - - // Encode Type - der.push(0x02); - - // Encode Length - let value_bytes = self.to_bytes_be(); - let length_byte_count = value_bytes - .iter() - .position(|&b| b != 0) - .map_or(0, |pos| value_bytes.len() - pos); - - // Always use long form for the length representation - let length_bytes = length_byte_count.to_be_bytes(); - der.push((0x80 | length_bytes.len()) as u8); // Set the most significant bit to indicate long form - der.extend_from_slice(&length_bytes); - - // Encode Value - der.extend_from_slice(&value_bytes); - - der - } -} diff --git a/okamoto-uchiyama-rs/tests/test_pem.rs b/okamoto-uchiyama-rs/tests/test_pem.rs index 884b848..c1fcac9 100644 --- a/okamoto-uchiyama-rs/tests/test_pem.rs +++ b/okamoto-uchiyama-rs/tests/test_pem.rs @@ -16,7 +16,7 @@ fn test_public_key_pem_encoding() { // Expected PEM-encoded string let expected_pem = "-----BEGIN PUBLIC KEY-----\n\ - AgUCMjR0xwIFAeHTk/cCBQHcIAPx\n\ + MBUCBQIyNHTHAgUB4dOT9wIFAdwgA/E=\n\ -----END PUBLIC KEY-----\n"; // Assert equality @@ -42,7 +42,7 @@ fn test_private_key_pem_encoding() { // Expected PEM-encoded string let expected_pem = "-----BEGIN PRIVATE KEY-----\n\ - AgUCMjR0xwIFAeHTk/cCBQHcIAPxAgMfY3oCAgfTAgIJLwIDPTfp\n\ + MCcCBQIyNHTHAgUB4dOT9wIFAdwgA/ECAx9jegICB9MCAgkvAgM9N+k=\n\ -----END PRIVATE KEY-----\n"; // Assert equality From bc7a3583f50930601c185a625e2fb3b3a65eb151 Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Sat, 17 Feb 2024 02:16:36 +0100 Subject: [PATCH 08/13] Enable parsing of PEM-encoded public keys to `PublicKey` objects --- .../examples/parse_public_key_pem.rs | 15 ++++++ okamoto-uchiyama-rs/src/crypto/public_key.rs | 54 +++++++++++++++++++ okamoto-uchiyama-rs/src/error.rs | 4 ++ 3 files changed, 73 insertions(+) create mode 100644 okamoto-uchiyama-rs/examples/parse_public_key_pem.rs diff --git a/okamoto-uchiyama-rs/examples/parse_public_key_pem.rs b/okamoto-uchiyama-rs/examples/parse_public_key_pem.rs new file mode 100644 index 0000000..eee461b --- /dev/null +++ b/okamoto-uchiyama-rs/examples/parse_public_key_pem.rs @@ -0,0 +1,15 @@ +use okamoto_uchiyama::PublicKey; + +fn main() { + // Define the PEM-encoded public key string + let pem_encoded_key = " + -----BEGIN PUBLIC KEY-----\n\ + MBUCBQIyNHTHAgUB4dOT9wIFAdwgA/E=\n\ + -----END PUBLIC KEY-----\n"; + + // Attempt to parse the PEM-encoded key into a PublicKey instance + let public_key = PublicKey::from_pem(pem_encoded_key).unwrap(); + + // Print the public key + println!("{}", public_key); +} diff --git a/okamoto-uchiyama-rs/src/crypto/public_key.rs b/okamoto-uchiyama-rs/src/crypto/public_key.rs index a3c90a2..92c4552 100644 --- a/okamoto-uchiyama-rs/src/crypto/public_key.rs +++ b/okamoto-uchiyama-rs/src/crypto/public_key.rs @@ -2,7 +2,9 @@ use crate::error::OkamotoUchiyamaError; use crate::pem::PemEncodable; use asn1::BigUint as Asn1BigUint; +use asn1::ParseError; use base64::engine::general_purpose; +use base64::engine::general_purpose::STANDARD; use base64::Engine; use num::One; use num_bigint_dig::BigUint; @@ -31,6 +33,58 @@ impl PublicKey { } } + /// Decode a PEM-encoded public key string into a PublicKey instance + pub fn from_pem(pem: &str) -> Result { + // Trim the starting and ending spaces/newlines + let pem = pem.trim(); + + // Check if the PEM string starts and ends with the correct tags + if !pem.starts_with("-----BEGIN PUBLIC KEY-----") + || !pem.ends_with("-----END PUBLIC KEY-----") + { + return Err(OkamotoUchiyamaError::PemDecodingError); + } + + // Extract the base64-encoded ASN.1 sequence between the tags + let base64_encoded = pem + .trim_start_matches("-----BEGIN PUBLIC KEY-----") + .trim_end_matches("-----END PUBLIC KEY-----") + .trim(); + + // Decode the base64-encoded ASN.1 sequence using Engine::decode + let asn1_decoded = STANDARD + .decode(base64_encoded) + .map_err(|_| OkamotoUchiyamaError::PemDecodingError)?; + + // Parse the ASN.1 sequence into the PublicKey struct + let (n, g, h) = + asn1::parse::<_, ParseError, _>(&asn1_decoded, |d: &mut asn1::Parser<'_>| { + d.read_element::()? + .parse::<_, ParseError, _>(|d| { + // Parse ASN.1 BigUint elements + let n_asn1 = d.read_element::()?; + let g_asn1 = d.read_element::()?; + let h_asn1 = d.read_element::()?; + + // Convert ASN.1 BigUint to BigUint + let n_bytes = n_asn1.as_bytes(); + let g_bytes = g_asn1.as_bytes(); + let h_bytes = h_asn1.as_bytes(); + + // Convert bytes back to BigUint + let n = BigUint::from_bytes_be(&n_bytes); + let g = BigUint::from_bytes_be(&g_bytes); + let h = BigUint::from_bytes_be(&h_bytes); + + Ok((n, g, h)) + }) + }) + .map_err(|_| OkamotoUchiyamaError::PemDecodingError)?; + + // Create and return PublicKey instance + Ok(PublicKey::new(&n, &g, &h)) + } + /// Performs homomorphic operation over two passed ciphers. /// Okamoto-Uchiyama has additive homomorphic property, so resultant cipher /// contains the sum of two numbers. diff --git a/okamoto-uchiyama-rs/src/error.rs b/okamoto-uchiyama-rs/src/error.rs index deab8e5..38f7a6d 100644 --- a/okamoto-uchiyama-rs/src/error.rs +++ b/okamoto-uchiyama-rs/src/error.rs @@ -10,6 +10,10 @@ pub enum OkamotoUchiyamaError { #[error("Message is larger than public key size")] CipherTooLarge, + // When the PEM key decoding fails + #[error("Error when decoding the PEM encoded key")] + PemDecodingError, + // Generic error message #[error("Okamoto-Uchiyama failed with the following stdout: {stdout} stderr: {stderr}")] OkamotoUchiyamaError { stdout: String, stderr: String }, From 1490e72cbd32971f237e24bd09a05f8e97af645a Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Sun, 18 Feb 2024 19:29:18 +0100 Subject: [PATCH 09/13] Implement from_pem function for `PrivateKey` --- .../examples/parse_private_key_pem.rs | 15 ++++ okamoto-uchiyama-rs/src/crypto/private_key.rs | 73 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 okamoto-uchiyama-rs/examples/parse_private_key_pem.rs diff --git a/okamoto-uchiyama-rs/examples/parse_private_key_pem.rs b/okamoto-uchiyama-rs/examples/parse_private_key_pem.rs new file mode 100644 index 0000000..c3e86f6 --- /dev/null +++ b/okamoto-uchiyama-rs/examples/parse_private_key_pem.rs @@ -0,0 +1,15 @@ +use okamoto_uchiyama::PrivateKey; + +fn main() { + // Define the PEM-encoded private key string + let pem_encoded_key = " + -----BEGIN PRIVATE KEY-----\n\ + MCcCBQIyNHTHAgUB4dOT9wIFAdwgA/ECAx9jegICB9MCAgkvAgM9N+k=\n\ + -----END PRIVATE KEY-----\n"; + + // Attempt to parse the PEM-encoded key into a PrivateKey instance + let private_key = PrivateKey::from_pem(pem_encoded_key).unwrap(); + + // Print the private key + println!("{}", private_key); +} diff --git a/okamoto-uchiyama-rs/src/crypto/private_key.rs b/okamoto-uchiyama-rs/src/crypto/private_key.rs index 9fa13b1..58f4289 100644 --- a/okamoto-uchiyama-rs/src/crypto/private_key.rs +++ b/okamoto-uchiyama-rs/src/crypto/private_key.rs @@ -1,5 +1,7 @@ use crate::crypto::okamoto_uchiyama::PublicKey; +use crate::error::OkamotoUchiyamaError; use crate::pem::PemEncodable; + use asn1::BigUint as Asn1BigUint; use base64::{engine::general_purpose, Engine as _}; use num_bigint_dig::BigUint; @@ -40,6 +42,77 @@ impl PrivateKey { p_squared, } } + + /// Decode a PEM-encoded private key string into a PrivateKey instance + pub fn from_pem(pem: &str) -> Result { + // Trim the starting and ending spaces/newlines + let pem = pem.trim(); + + // Check if the PEM string starts and ends with the correct tags + if !pem.starts_with("-----BEGIN PRIVATE KEY-----") + || !pem.ends_with("-----END PRIVATE KEY-----") + { + return Err(OkamotoUchiyamaError::PemDecodingError); + } + + // Extract the base64-encoded ASN.1 sequence between the tags + let base64_encoded = pem + .trim_start_matches("-----BEGIN PRIVATE KEY-----") + .trim_end_matches("-----END PRIVATE KEY-----") + .trim(); + + // Decode the base64-encoded ASN.1 sequence using Engine::decode + let asn1_decoded = general_purpose::STANDARD + .decode(base64_encoded) + .map_err(|_| OkamotoUchiyamaError::PemDecodingError)?; + + // Parse the ASN.1 sequence into the PrivateKey struct + let (n, g, h, gd, p, q, p_squared) = + asn1::parse::<_, asn1::ParseError, _>(&asn1_decoded, |d: &mut asn1::Parser<'_>| { + d.read_element::()? + .parse::<_, asn1::ParseError, _>(|d| { + // Parse ASN.1 BigUint elements + let n_asn1 = d.read_element::()?; + let g_asn1 = d.read_element::()?; + let h_asn1 = d.read_element::()?; + let gd_asn1 = d.read_element::()?; + let p_asn1 = d.read_element::()?; + let q_asn1 = d.read_element::()?; + let p_squared_asn1 = d.read_element::()?; + + // Convert ASN.1 BigUint to BigUint + let n_bytes = n_asn1.as_bytes(); + let g_bytes = g_asn1.as_bytes(); + let h_bytes = h_asn1.as_bytes(); + let gd_bytes = gd_asn1.as_bytes(); + let p_bytes = p_asn1.as_bytes(); + let q_bytes = q_asn1.as_bytes(); + let p_squared_bytes = p_squared_asn1.as_bytes(); + + // Convert bytes back to BigUint + let n = BigUint::from_bytes_be(&n_bytes); + let g = BigUint::from_bytes_be(&g_bytes); + let h = BigUint::from_bytes_be(&h_bytes); + let gd = BigUint::from_bytes_be(&gd_bytes); + let p = BigUint::from_bytes_be(&p_bytes); + let q = BigUint::from_bytes_be(&q_bytes); + let p_squared = BigUint::from_bytes_be(&p_squared_bytes); + + Ok((n, g, h, gd, p, q, p_squared)) + }) + }) + .map_err(|_| OkamotoUchiyamaError::PemDecodingError)?; + + // Create and return PrivateKey instance + let public_key = PublicKey::new(&n, &g, &h); + Ok(PrivateKey { + public_key, + gd, + p, + q, + p_squared, + }) + } } // Implementation of the Display trait for the PrivateKey struct From 4abf5710a99ccbd27ee0d4dea9bb28681c165137 Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Mon, 19 Feb 2024 23:21:00 +0100 Subject: [PATCH 10/13] Add unit tests for the `from_pem` method --- okamoto-uchiyama-rs/tests/test_pem.rs | 45 +++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/okamoto-uchiyama-rs/tests/test_pem.rs b/okamoto-uchiyama-rs/tests/test_pem.rs index c1fcac9..8288202 100644 --- a/okamoto-uchiyama-rs/tests/test_pem.rs +++ b/okamoto-uchiyama-rs/tests/test_pem.rs @@ -48,3 +48,48 @@ fn test_private_key_pem_encoding() { // Assert equality assert_eq!(pem_str, expected_pem); } + +#[test] +fn test_parse_public_key_from_pem() { + // Define the PEM-encoded public key string + let pem_str = "-----BEGIN PUBLIC KEY-----\n\ + MBUCBQIyNHTHAgUB4dOT9wIFAdwgA/E=\n\ + -----END PUBLIC KEY-----\n"; + + // Parse the PEM-encoded string into a PublicKey instance + let parsed_public_key = PublicKey::from_pem(pem_str).unwrap(); + + let expected_public_key = PublicKey::new( + &BigUint::from(9432233159u64), + &BigUint::from(8083706871u64), + &BigUint::from(7988052977u64), + ); + + // // Assert equality between the parsed and expected public keys + assert_eq!(parsed_public_key, expected_public_key); +} + +#[test] +fn test_parse_private_key_from_pem() { + // Define the PEM-encoded private key string + let pem_str = "-----BEGIN PRIVATE KEY-----\n\ + MCcCBQIyNHTHAgUB4dOT9wIFAdwgA/ECAx9jegICB9MCAgkvAgM9N+k=\n\ + -----END PRIVATE KEY-----\n"; + + // Parse the PEM-encoded string into a PrivateKey instance + let parsed_private_key = PrivateKey::from_pem(pem_str).unwrap(); + + let public_key = PublicKey::new( + &BigUint::from(9432233159u64), + &BigUint::from(8083706871u64), + &BigUint::from(7988052977u64), + ); + let expected_private_key = PrivateKey::new( + &public_key, + &BigUint::from(2003u64), + &BigUint::from(2351u64), + ); + + // Assert equality between the parsed and expected private keys + assert_eq!(parsed_private_key, expected_private_key); +} From ee90b8871c8f375e088453639b7d80679cc15b9f Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Tue, 20 Feb 2024 22:55:53 +0100 Subject: [PATCH 11/13] Encapsulate Ciphertexts in Ciphertext Struct --- okamoto-uchiyama-rs/src/crypto/ciphertext.rs | 19 ++++++++ okamoto-uchiyama-rs/src/crypto/mod.rs | 1 + .../src/crypto/okamoto_uchiyama.rs | 22 ++++++---- okamoto-uchiyama-rs/src/crypto/public_key.rs | 43 ++++++++++--------- 4 files changed, 56 insertions(+), 29 deletions(-) create mode 100644 okamoto-uchiyama-rs/src/crypto/ciphertext.rs diff --git a/okamoto-uchiyama-rs/src/crypto/ciphertext.rs b/okamoto-uchiyama-rs/src/crypto/ciphertext.rs new file mode 100644 index 0000000..f97956c --- /dev/null +++ b/okamoto-uchiyama-rs/src/crypto/ciphertext.rs @@ -0,0 +1,19 @@ +use num_bigint_dig::BigUint; + +// Define a Ciphertext struct to encapsulate a ciphertext value +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Ciphertext { + value: BigUint, +} + +impl Ciphertext { + // Constructor function to create a new Ciphertext instance + pub fn new(value: BigUint) -> Self { + Ciphertext { value } + } + + // Getter method to retrieve the value of the ciphertext + pub fn value(&self) -> &BigUint { + &self.value + } +} diff --git a/okamoto-uchiyama-rs/src/crypto/mod.rs b/okamoto-uchiyama-rs/src/crypto/mod.rs index 52f76a1..69b0f85 100644 --- a/okamoto-uchiyama-rs/src/crypto/mod.rs +++ b/okamoto-uchiyama-rs/src/crypto/mod.rs @@ -1,3 +1,4 @@ +pub mod ciphertext; pub mod okamoto_uchiyama; pub mod private_key; pub mod public_key; diff --git a/okamoto-uchiyama-rs/src/crypto/okamoto_uchiyama.rs b/okamoto-uchiyama-rs/src/crypto/okamoto_uchiyama.rs index a07a3d0..d4d454a 100644 --- a/okamoto-uchiyama-rs/src/crypto/okamoto_uchiyama.rs +++ b/okamoto-uchiyama-rs/src/crypto/okamoto_uchiyama.rs @@ -6,6 +6,7 @@ use num_bigint_dig::{BigUint, RandBigInt}; use num_primes::Generator; use rand::thread_rng; +use crate::crypto::ciphertext::Ciphertext; pub use crate::crypto::private_key::PrivateKey; pub use crate::crypto::public_key::PublicKey; @@ -43,14 +44,14 @@ impl OkamotoUchiyama { }; // Calculate a large prime number with `length / 3` bit length - let p_prime = Generator::new_prime((&length / 3) as usize); + let p_prime = Generator::new_prime((length / 3) as usize); // Convert the prime number to BigUint - let p = BigUint::from_bytes_be(&p_prime.clone().to_bytes_be()); + let p = BigUint::from_bytes_be(&p_prime.to_bytes_be()); // Calculate another large prime number with `length / 2` bit length - let q_prime = Generator::new_prime((&length / 2) as usize); + let q_prime = Generator::new_prime((length / 2) as usize); // Convert the prime number to BigUint - let q = BigUint::from_bytes_be(&q_prime.clone().to_bytes_be()); + let q = BigUint::from_bytes_be(&q_prime.to_bytes_be()); // Calculate n = p^2 * q let p_squared = &p * &p; @@ -113,23 +114,26 @@ impl OkamotoUchiyama { } /// Encrypt a message using the public key. - pub fn encrypt(message: &BigUint, public_key: &PublicKey) -> BigUint { + pub fn encrypt(message: &BigUint, public_key: &PublicKey) -> Ciphertext { // Choose a random integer r from {1...n-1}. let mut rng = thread_rng(); let n_minus_1 = &public_key.n - &BigUint::one(); let r = rng.gen_biguint_range(&BigUint::one(), &n_minus_1); // Compute the ciphertext as c = (g^m * h^r) mod n. - (public_key.g.modpow(&message, &public_key.n) * public_key.h.modpow(&r, &public_key.n)) - % &public_key.n + let ciphertext_value = (public_key.g.modpow(&message, &public_key.n) + * public_key.h.modpow(&r, &public_key.n)) + % &public_key.n; + + Ciphertext::new(ciphertext_value) } /// Decrypts a ciphertext using the provided private key. - pub fn decrypt(ciphertext: &BigUint, private_key: &PrivateKey) -> BigUint { + pub fn decrypt(ciphertext: &Ciphertext, private_key: &PrivateKey) -> BigUint { let pminus1 = &private_key.p - 1u32; // c^(p-1) mod p^2 - let a = ciphertext.modpow(&pminus1, &private_key.p_squared); + let a = ciphertext.value().modpow(&pminus1, &private_key.p_squared); // L1(a) = (a - 1) / p let l1 = (a - 1u32) / &private_key.p.clone(); diff --git a/okamoto-uchiyama-rs/src/crypto/public_key.rs b/okamoto-uchiyama-rs/src/crypto/public_key.rs index 92c4552..3e8d49a 100644 --- a/okamoto-uchiyama-rs/src/crypto/public_key.rs +++ b/okamoto-uchiyama-rs/src/crypto/public_key.rs @@ -1,3 +1,4 @@ +use crate::crypto::ciphertext::Ciphertext; use crate::error::OkamotoUchiyamaError; use crate::pem::PemEncodable; @@ -85,40 +86,42 @@ impl PublicKey { Ok(PublicKey::new(&n, &g, &h)) } - /// Performs homomorphic operation over two passed ciphers. - /// Okamoto-Uchiyama has additive homomorphic property, so resultant cipher + /// Performs homomorphic operation over two passed ciphertexts. + /// Okamoto-Uchiyama has additive homomorphic property, so the resultant ciphertext /// contains the sum of two numbers. pub fn homomorphic_encrypt_two( - self, - c1: &BigUint, - c2: &BigUint, - ) -> Result { - if c1 == &self.n || c2 == &self.n { + &self, + c1: &Ciphertext, + c2: &Ciphertext, + ) -> Result { + if c1.value() == &self.n || c2.value() == &self.n { return Err(OkamotoUchiyamaError::CipherTooLarge); } - // Calculate the product of the two ciphers and take the modulus by the public key n. - Ok((c1 * c2) % &self.n) + // Calculate the product of the two ciphertexts and take the modulus by the public key n. + let result_value = (c1.value() * c2.value()) % &self.n; + Ok(Ciphertext::new(result_value)) } - /// Performs homomorphic operation over multiple passed ciphers. - /// Okamoto-Uchiyama has additive homomorphic property, so resultant cipher + /// Performs homomorphic operation over multiple passed ciphertexts. + /// Okamoto-Uchiyama has additive homomorphic property, so the resultant ciphertext /// contains the sum of multiple numbers. pub fn homomorphic_encrypt_multiple( - self, - ciphers: Vec<&BigUint>, - ) -> Result { - // Check if any cipher in the vector is equal to the public key n. - if ciphers.contains(&&self.n) { + &self, + ciphers: Vec<&Ciphertext>, + ) -> Result { + // Check if any ciphertext in the vector has the same value as the public key n. + if ciphers.iter().any(|&cipher| cipher.value() == &self.n) { return Err(OkamotoUchiyamaError::CipherTooLarge); } - // Calculate the product of all ciphers in the vector and return it. - let mut c = BigUint::one(); + // Calculate the product of all ciphertexts in the vector and return it. + let mut result = BigUint::one(); for cipher in ciphers { - c = &c * cipher; + result = &result * cipher.value(); } - Ok(c) + let result_value = result % &self.n; + Ok(Ciphertext::new(result_value)) } } From a3c05daa255b7fa956709d813e2bc310a74e93c9 Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Wed, 21 Feb 2024 01:33:47 +0100 Subject: [PATCH 12/13] Implement ciphertext loading from PEM --- okamoto-uchiyama-rs/src/crypto/ciphertext.rs | 62 ++++++++++++++++++++ okamoto-uchiyama-rs/tests/test_pem.rs | 34 +++++++++++ 2 files changed, 96 insertions(+) diff --git a/okamoto-uchiyama-rs/src/crypto/ciphertext.rs b/okamoto-uchiyama-rs/src/crypto/ciphertext.rs index f97956c..12ee8e4 100644 --- a/okamoto-uchiyama-rs/src/crypto/ciphertext.rs +++ b/okamoto-uchiyama-rs/src/crypto/ciphertext.rs @@ -1,3 +1,9 @@ +use crate::error::OkamotoUchiyamaError; + +use crate::pem::PemEncodable; +use asn1::BigUint as Asn1BigUint; +use base64::engine::general_purpose; +use base64::Engine; use num_bigint_dig::BigUint; // Define a Ciphertext struct to encapsulate a ciphertext value @@ -12,8 +18,64 @@ impl Ciphertext { Ciphertext { value } } + /// Attempt to create a `Ciphertext` from a PEM-encoded string + pub fn from_pem(pem: &str) -> Result { + // Trim the starting and ending spaces/newlines + let pem = pem.trim(); + + // Check if the PEM string starts and ends with the correct tags + if !pem.starts_with("-----BEGIN CIPHERTEXT-----") + || !pem.ends_with("-----END CIPHERTEXT-----") + { + return Err(OkamotoUchiyamaError::PemDecodingError); + } + + // Extract the base64-encoded ASN.1 sequence between the tags + let base64_encoded = pem + .trim_start_matches("-----BEGIN CIPHERTEXT-----") + .trim_end_matches("-----END CIPHERTEXT-----") + .trim(); + + // Decode the base64-encoded ASN.1 sequence using Engine::decode + let asn1_decoded = general_purpose::STANDARD + .decode(base64_encoded.as_bytes()) + .map_err(|_| OkamotoUchiyamaError::PemDecodingError)?; + + // Parse the ASN.1 sequence into a BigUint + let value_asn1 = asn1::parse_single::(&asn1_decoded) + .map_err(|_| OkamotoUchiyamaError::PemDecodingError)?; + + // Convert the ASN.1 BigUint to a BigUint + let value_bytes = value_asn1.as_bytes(); + let value = BigUint::from_bytes_be(&value_bytes); + + // Return a new `Ciphertext` instance + Ok(Ciphertext::new(value)) + } + // Getter method to retrieve the value of the ciphertext pub fn value(&self) -> &BigUint { &self.value } } + +/// Implement the PemEncodable trait for the Ciphertext struct +impl PemEncodable for Ciphertext { + fn to_pem(&self) -> String { + let mut pem = String::new(); + + // Convert the ciphertext value to ASN.1 + let value_bytes = self.value.to_bytes_be(); + let value_asn1 = Asn1BigUint::new(&value_bytes); + + // Write the value to ASN.1 Sequence + let result = asn1::write(|w| w.write_element(&value_asn1)); + + // Encode the ASN.1 sequence using Base64 + pem.push_str("-----BEGIN CIPHERTEXT-----\n"); + pem.push_str(&general_purpose::STANDARD.encode(result.unwrap_or_else(|_| vec![]))); + pem.push_str("\n-----END CIPHERTEXT-----\n"); + + pem + } +} diff --git a/okamoto-uchiyama-rs/tests/test_pem.rs b/okamoto-uchiyama-rs/tests/test_pem.rs index 8288202..5590aa9 100644 --- a/okamoto-uchiyama-rs/tests/test_pem.rs +++ b/okamoto-uchiyama-rs/tests/test_pem.rs @@ -1,4 +1,5 @@ use num_bigint_dig::BigUint; +use okamoto_uchiyama::crypto::ciphertext::Ciphertext; use okamoto_uchiyama::pem::PemEncodable; use okamoto_uchiyama::{PrivateKey, PublicKey}; @@ -93,3 +94,36 @@ fn test_parse_private_key_from_pem() { // Assert equality between the parsed and expected private keys assert_eq!(parsed_private_key, expected_private_key); } + +#[test] +fn test_ciphertext_pem_encoding() { + // Create a sample ciphertext + let ciphertext = Ciphertext::new(BigUint::from(1234567890u64)); + + // Encode to PEM + let pem_str = ciphertext.to_pem(); + + // Expected PEM-encoded string + let expected_pem = "-----BEGIN CIPHERTEXT-----\n\ + AgRJlgLS\n\ + -----END CIPHERTEXT-----\n"; + + // Assert equality + assert_eq!(pem_str, expected_pem); +} + +#[test] +fn test_parse_ciphertext_from_pem() { + // Define the PEM-encoded ciphertext string + let pem_str = "-----BEGIN CIPHERTEXT-----\n\ + AgRJlgLS\n\ + -----END CIPHERTEXT-----\n"; + + // Parse the PEM-encoded string into a Ciphertext instance + let parsed_ciphertext = Ciphertext::from_pem(pem_str).unwrap(); + + let expected_ciphertext = Ciphertext::new(BigUint::from(1234567890u64)); + + // Assert equality between the parsed and expected ciphertexts + assert_eq!(parsed_ciphertext, expected_ciphertext); +} From 278de0301cf8a58b9ead4091f01e6d983de5445a Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Thu, 22 Feb 2024 02:13:24 +0100 Subject: [PATCH 13/13] Update rust.yml --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d54fa2f..4793edf 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -17,6 +17,6 @@ jobs: steps: - uses: actions/checkout@v3 - name: Build - run: cargo build --verbose + run: cargo build --manifest-path=./okamoto-uchiyama-rs/Cargo.toml --verbose - name: Run tests - run: cargo test --verbose + run: cargo test --manifest-path=./okamoto-uchiyama-rs/Cargo.toml --verbose