// SPDX-FileCopyrightText: 2023-2024 eaon // SPDX-License-Identifier: AGPL-3.0-or-later use ecdh_omr::*; use rand::seq::SliceRandom; use rand_core::OsRng; use x25519_dalek::*; type Hint = ecdh_omr::Hint, 32>; fn main() { // Bob generates a secret on their client let bob_secret = StaticSecret::random_from_rng(&mut OsRng); // Bob's public key makes it to Alice (this scheme does not concern itself with this problem). // For the sake of this example, Bob does not have Alice's public key yet. let bob_public = PublicKey::from(&bob_secret); // Alice blinds Bob's public key, in such a way that they can leave a message with a server that // won't know about Bob's real public key. // In this example, Alice's message is their public key, so that Alice and Bob can establish // communication over a different channel. let public_key_blinded_by_alice = bob_public.blind(&mut OsRng); let alice_secret = StaticSecret::random_from_rng(&mut OsRng); let alice_public = PublicKey::from(&alice_secret); let alice_message = alice_public.as_bytes(); // The server always shares the same amount of hints with whoever requests them. To make up for // missing "real" hints, it generates decoys which should look indistinguishable from the real // deal let mut hints_served = Vec::with_capacity(5000); for _ in 0..4999 { hints_served.push(Hint::random_decoy(&mut OsRng)); } // Whenever anyone requests hints, the server creates new hints for every message by using the // blinded public key that was submitted along side it. Due to the random input, every time a // hint is created, its bytes are different for every request, even though the recipient and // message stay the same. let server_request_secret = StaticSecret::random_from_rng(&mut OsRng); hints_served.push( Hint::from_blinding_factor_secret( &server_request_secret, &public_key_blinded_by_alice, &alice_message, &mut OsRng, ) .unwrap(), ); // We don't want the server to serve messages in a predictable order, so we shuffle real hints // and decoy hints hints_served.shuffle(&mut OsRng); // Bob now attempts to decrypt every hint they received, collecting the successfully decrypted // ones. As all that work happens on the client side, the server is oblivious as to who // requested messages or if they were able to recover anything. let bob_recovered_messages = bob_secret.take_all_the(&hints_served); assert_eq!(&bob_recovered_messages[0], alice_message); println!("Bob successfully recovered Alice's message"); }