commit 94006c220ff1bb581ae4de7d9b7f5f9dbd4f509f Author: Chris Tam Date: Thu Aug 1 11:25:35 2024 +0800 init in gitea diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5042f93 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,115 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "dske_poc" +version = "0.1.0" +dependencies = [ + "bitvec", + "rand", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..98b628b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "dske_poc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bitvec = "1.0.1" +rand = "0.8.5" diff --git a/README.md b/README.md new file mode 100644 index 0000000..a56c43a --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# DSKE Poc + +## Introduction + +This is a proof of concept of the DSKE system, the main goal is to demonstrate the algorithm in the DSKE. There are a few assumptions are limitations in this proof-of-concept project. + +1. This is not a working protocol, this project is just to prove the algorithm concept. +2. This project is demonstrating a simple DSKE protocol, which means n = k. It is a (n, n) secret sharing scheme. +3. The code assumes that the final key sender (Alice) chooses all her security hubs and all her security hubs include the receiver (Bob) as another client. +4. The code assumes that all the random bits transfer successfully. Bob won't abort the protocol in the key construction phase. +5. The code assumes every security hub and client won't run out of random bits. + +## DSKE Main Phases + +1. PSKM generation and distribution + + the `SecurityHub.register_client` method will generate random bits, store it and return the clone of the random bits. `Client.register_securityhub` method will store the random bits clone so that the security hub and the client will have their copy of the random bits. + +2. Peer identity establishment + + For this proof-of-concept project, the code assumes that the sender (Alice) and the receiver (Bob) are registered in the same security hubs. + +3. Key agreement + + 1. Share generation + + The proof-of-concept project will use (n, n) secret sharing scheme in a simple DSKE protocol, using all the security hubs the user registered. The final key S can be checked by the method `Client.generate_final_key`. + + 2. Share distribution + + Each security hub will use the method `SecurityHub.generate_key_instruction` to generate a key instruction A (Alice's random bits) XOR B (Bob's random bits). + + 3. Key Reconstruction + + Bob can use the method `Client.retrieve_key` to calculate A ^ B ^ B to retrieve A in each share. Bob can reconstruct the final key S by executing XOR in every share in the simple DSKE protocol (n, n) secret sharing scheme. + +4. Key validation + + The proof-of-concept project assumes all the share's key instructions are delivered correctly, the simple DSKE protocol with (n, n) secret sharing scheme won't be abort in this proof-of-concept project. diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a8bb72b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,201 @@ +use std::collections::HashMap; +use rand::Rng; +use bitvec::prelude::BitVec; + +struct PSKM { + random_bits: BitVec, + usage: i32, +} + +struct SecurityHub { + name: String, + client_hashmap: HashMap, +} + +impl SecurityHub { + fn new(name: &str) -> SecurityHub { + SecurityHub { + name: String::from(name), + client_hashmap: HashMap::new() + } + } + + // generate random bits and pre-share to client + fn register_client(&mut self, client_name: String) -> BitVec { + let mut rng = rand::thread_rng(); + + // number of random bits to generate + let num_bits = 100; + + let mut random_bits: BitVec = BitVec::with_capacity(num_bits); + + // generate and store the random bits + for _ in 0..num_bits { + let random_bit: u8 = rng.gen_range(0..=1); + random_bits.push(random_bit == 1); + } + + // store the random bit in security hub + self.client_hashmap.insert(client_name, PSKM { + random_bits: random_bits.clone(), + usage: 0, + }); + + // return the clone of random bit to the client + return random_bits.clone(); + } + + // calculate A ^ B + fn generate_key_instruction(&mut self, sender: String, receiver: String, key_size: i32) -> BitVec{ + // get the random bits of the sender + let sender_pskm = self.client_hashmap.get(&sender).unwrap(); + // get the random bits of the receiver + let receiver_pskm = self.client_hashmap.get(&receiver).unwrap(); + + let mut key_instruction: BitVec = BitVec::with_capacity(key_size as usize); + + // calculate key instruction A ^ B + for i in 0..key_size { + let sender_bit = if *sender_pskm.random_bits.get((i + sender_pskm.usage) as usize).unwrap() { true } else { false }; + let receiver_bit = if *receiver_pskm.random_bits.get((i + receiver_pskm.usage) as usize).unwrap() { true } else { false }; + let xor_result = sender_bit ^ receiver_bit; + key_instruction.push(xor_result); + } + + // increate the random bits usage by key_size + let sender_pskm = self.client_hashmap.get_mut(&sender).unwrap(); + sender_pskm.usage += key_size; + + let receiver_pskm = self.client_hashmap.get_mut(&receiver).unwrap(); + receiver_pskm.usage += key_size; + + return key_instruction; + } + +} + +struct Client { + name: String, + securityhub_hashmap: HashMap, +} + +impl Client { + fn new(name: &str) -> Client { + Client { + name: String::from(name), + securityhub_hashmap: HashMap::new() + } + } + + // store the random bits from security hub + fn register_securityhub(&mut self, security_hub_name: String, random_bits: BitVec) { + self.securityhub_hashmap.insert(security_hub_name, PSKM { + random_bits, + usage: 0, + }); + } + + // using the random bits by every security hub and calculate the XOR result + fn generate_final_key(&mut self, key_size: i32) -> BitVec { + let mut final_key: BitVec = BitVec::with_capacity(key_size as usize); + for i in 0..key_size { + let mut xor_result: bool = false; // pre-assign the value to false just because the compiler is warning + // the the random bits from every security hub, calculate the XOR result + for (index, (_, pskm)) in self.securityhub_hashmap.iter().enumerate() { + let bit_value = if *pskm.random_bits.get(i as usize + pskm.usage as usize).unwrap() { true } else { false }; + if index == 0 { + xor_result = bit_value + } else { + xor_result = xor_result ^ bit_value + } + } + final_key.push(xor_result); + } + + // for every security hub PSKM, increment the usage by key_size + for (_, pskm) in self.securityhub_hashmap.iter_mut() { + pskm.usage += key_size; + } + return final_key; + } + + // calculate A ^ B ^ B + fn retrieve_key(&mut self, security_hub_name: String, key_instruction: BitVec, _sender: String) -> BitVec { + let mut key: BitVec = BitVec::with_capacity(key_instruction.len()); + let pskm = self.securityhub_hashmap.get_mut(&security_hub_name).unwrap(); + for i in 0..key_instruction.len() { + let bit_value = if *pskm.random_bits.get((i as usize + pskm.usage as usize) as usize).unwrap() { true } else { false }; + let xor_result = bit_value ^ *key_instruction.get(i).unwrap(); + key.push(xor_result); + } + pskm.usage += key_instruction.len() as i32; + return key; + } +} + +fn register_client_to_security_hub(security_hub: &mut SecurityHub, client: &mut Client) { + let random_bits = security_hub.register_client(client.name.clone()); + client.register_securityhub(security_hub.name.clone(), random_bits); +} + +fn main() { + let mut security_hub_hash_map: HashMap = HashMap::new(); + security_hub_hash_map.insert(String::from("SH1"), SecurityHub::new("SH1")); + security_hub_hash_map.insert(String::from("SH2"), SecurityHub::new("SH2")); + security_hub_hash_map.insert(String::from("SH3"), SecurityHub::new("SH3")); + + + let mut client_hash_map: HashMap = HashMap::new(); + client_hash_map.insert(String::from("Alice"), Client::new("Alice")); + client_hash_map.insert(String::from("Bob"), Client::new("Bob")); + + //register client to security hub, pre-share the random bits from security to client + register_client_to_security_hub(security_hub_hash_map.get_mut("SH1").unwrap(), client_hash_map.get_mut("Alice").unwrap()); + register_client_to_security_hub(security_hub_hash_map.get_mut("SH2").unwrap(), client_hash_map.get_mut("Alice").unwrap()); + register_client_to_security_hub(security_hub_hash_map.get_mut("SH3").unwrap(), client_hash_map.get_mut("Alice").unwrap()); + + register_client_to_security_hub(security_hub_hash_map.get_mut("SH1").unwrap(), client_hash_map.get_mut("Bob").unwrap()); + register_client_to_security_hub(security_hub_hash_map.get_mut("SH2").unwrap(), client_hash_map.get_mut("Bob").unwrap()); + register_client_to_security_hub(security_hub_hash_map.get_mut("SH3").unwrap(), client_hash_map.get_mut("Bob").unwrap()); + + // the final key size + let key_size = 20; + + // for simple DSKE protocol, generate XOR of every security hub random bits + let final_key_generate_by_alice = client_hash_map.get_mut("Alice").unwrap().generate_final_key(key_size); + let final_key_generate_by_alice_bits: String = final_key_generate_by_alice.iter().map(|bit| if *bit { "1" } else { "0" }).collect(); + println!("Final Key Generate by Alice: {}", final_key_generate_by_alice_bits); + + // generate key instruction in every security hub (assume all Alice's security hub include Bob) + let mut key_instruction_vec: Vec<(String, BitVec)> = Vec::new(); + for (_, (security_hub_name, _)) in client_hash_map.get("Alice").unwrap().securityhub_hashmap.iter().enumerate() { + let target_security_hub = String::from(security_hub_name); + let key_instruction = security_hub_hash_map.get_mut(security_hub_name).unwrap().generate_key_instruction(String::from("Alice"),String::from("Bob"), key_size); + key_instruction_vec.push((target_security_hub, key_instruction)); + } + + // for every key instruction, calculate A ^ B ^ B to retrieve A + let mut alice_bits: Vec = Vec::new(); + for (security_hub_name, key_instruction) in key_instruction_vec { + let alice_bits_retrieve_from_xor = client_hash_map.get_mut("Bob").unwrap().retrieve_key(security_hub_name, key_instruction, String::from("Alice")); + alice_bits.push(alice_bits_retrieve_from_xor); + } + + // reconstruct the final key + let mut final_key_reconstruct_by_bob: BitVec = BitVec::with_capacity(key_size as usize); + for i in 0..key_size { + let mut xor_result: bool = false; // pre-assign the value to false just because the compiler is warning + for (index, bit_vec) in alice_bits.iter().enumerate() { + let bit_value = if *bit_vec.get(i as usize).unwrap() { true } else { false }; + if index == 0 { + xor_result = bit_value + } else { + xor_result = xor_result ^ bit_value + } + } + final_key_reconstruct_by_bob.push(xor_result); + } + + let final_key_reconstruct_by_bob_bits: String = final_key_reconstruct_by_bob.iter().map(|bit| if *bit { "1" } else { "0" }).collect(); + println!("Final Key Reconstruct by Bob: {}", final_key_reconstruct_by_bob_bits); +} \ No newline at end of file