Define initial data structures

This commit is contained in:
Vecna 2023-11-28 13:18:08 -05:00
parent 7f375eaca8
commit e153d58fd2
2 changed files with 204 additions and 0 deletions

View File

@ -6,3 +6,16 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
array-bytes = "6.2.0"
bincode = "1"
curve25519-dalek = { version = "4", default-features = false, features = ["serde", "rand_core", "digest"] }
ed25519-dalek = { version = "2", features = ["serde", "rand_core"] }
lox-library = { git = "https://gitlab.torproject.org/tpo/anti-censorship/lox.git", version = "0.1.0" }
serde = "1.0.192"
serde_with = {version = "3.4.0", features = ["json"]}
sha1 = "0.10"
sha3 = "0.10"
time = "0.3.30"
# probably not needed once I can query an API
rand = { version = "0.8", features = ["std_rng"]}

191
src/lib.rs Normal file
View File

@ -0,0 +1,191 @@
use curve25519_dalek::scalar::Scalar;
use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
use lox_library::bridge_table::BridgeLine;
use serde::{Deserialize, Serialize};
use sha1::{Digest, Sha1};
use sha3::Sha3_256;
// for generating ed25519 keys during initial development
use rand::rngs::OsRng;
/// Get Julian date
pub fn today() -> u32 {
time::OffsetDateTime::now_utc()
.date()
.to_julian_day()
.try_into()
.unwrap()
}
/// Get bridge line for a bridge, requires some oracle
pub fn get_bridge_line(fingerprint: &[u8; 20]) -> BridgeLine {
// TODO
// for now just return empty bridgeline
BridgeLine::default()
}
/// Get verifying key for a bridge, requires some oracle
pub fn get_bridge_signing_pubkey(fingerprint: &[u8; 20]) -> VerifyingKey {
// TODO
// for now just return new pubkey
let mut csprng = OsRng {};
let keypair = SigningKey::generate(&mut csprng);
keypair.verifying_key()
}
/// Proof that the user knows (and should be able to access) a given bridge
#[derive(Serialize, Deserialize)]
pub enum ProofOfBridgeKnowledge {
/// Hash of bridge line as proof of knowledge of bridge line
HashOfBridgeLine { hash: [u8; 32] },
}
impl ProofOfBridgeKnowledge {
fn verify(&self, fingerprint: [u8; 20]) -> bool {
match *self {
ProofOfBridgeKnowledge::HashOfBridgeLine { ref hash } => {
let bl = get_bridge_line(&fingerprint);
let mut hasher = Sha3_256::new();
hasher.update(bincode::serialize(&bl).unwrap());
let bl_hash: [u8; 32] = hasher.finalize().into();
hash == &bl_hash
}
}
}
}
pub trait Report {
fn verify(&self) -> bool;
}
/// A report that the user was unable to connect to the bridge
#[derive(Serialize, Deserialize)]
pub struct NegativeUserReport {
/// hashed fingerprint (SHA-1 hash of 20-byte bridge ID)
pub fingerprint: [u8; 20],
/// some way to prove knowledge of bridge
pub bridge_pok: ProofOfBridgeKnowledge,
/// user's country code, may be an empty string
pub country: String,
/// today's Julian date
pub today: u32,
}
impl NegativeUserReport {
pub fn new(bridge_id: [u8; 20], bucket: Scalar, country: String) -> Self {
let mut hasher = Sha1::new();
hasher.update(bridge_id);
let fingerprint: [u8; 20] = hasher.finalize().into();
let mut hasher = Sha3_256::new();
hasher.update(bucket.to_bytes());
let bucket_hash: [u8; 32] = hasher.finalize().into();
let bridge_pok = ProofOfBridgeKnowledge::HashOfBridgeLine { hash: bucket_hash };
let today = today();
Self {
fingerprint,
bridge_pok,
country,
today,
}
}
}
impl Report for NegativeUserReport {
fn verify(&self) -> bool {
// possibly include check that self.today is recent as well
self.today <= today() && self.bridge_pok.verify(self.fingerprint)
}
}
/// A report that the user was able to connect to the bridge
#[derive(Serialize, Deserialize)]
pub struct PositiveUserReport {
/// hashed fingerprint (SHA-1 hash of 20-byte bridge ID)
pub fingerprint: [u8; 20],
/// token from the bridge indicating it was reached
pub bridge_token: BridgeToken,
// TODO: proof of level, something involving credential show
/// user's country code, may be an empty string
pub country: String,
/// today's Julian date
pub today: u32,
}
impl PositiveUserReport {
pub fn new(bridge_id: [u8; 20], bridge_token: BridgeToken, country: String) -> Self {
let mut hasher = Sha1::new();
hasher.update(bridge_id);
let fingerprint: [u8; 20] = hasher.finalize().into();
let today = today();
Self {
fingerprint,
bridge_token,
country,
today,
}
}
}
impl Report for PositiveUserReport {
fn verify(&self) -> bool {
// possibly include check that self.today is recent as well
self.today == self.bridge_token.unsigned_bridge_token.today
&& self.today <= today()
&& self.bridge_token.verify()
}
}
/// An unsigned token which indicates that the bridge was reached
#[derive(Serialize, Deserialize)]
pub struct UnsignedBridgeToken {
/// hashed fingerprint (SHA-1 hash of 20-byte bridge ID)
pub fingerprint: [u8; 20],
/// client's country code
pub country: String,
/// today's Julian date
pub today: u32,
}
impl UnsignedBridgeToken {
pub fn new(bridge_id: [u8; 20], country: String) -> Self {
let mut hasher = Sha1::new();
hasher.update(bridge_id);
let fingerprint: [u8; 20] = hasher.finalize().into();
let today = today();
Self {
fingerprint,
country,
today,
}
}
}
/// A signed token which indicates that the bridge was reached
#[derive(Serialize, Deserialize)]
pub struct BridgeToken {
/// the unsigned version of this token
pub unsigned_bridge_token: UnsignedBridgeToken,
/// signature from bridge's ed25519 key
pub sig: Signature,
}
impl BridgeToken {
pub fn new(unsigned_bridge_token: UnsignedBridgeToken, keypair: SigningKey) -> Self {
let sig = keypair.sign(&bincode::serialize(&unsigned_bridge_token).unwrap());
Self {
unsigned_bridge_token,
sig,
}
}
pub fn verify(&self) -> bool {
let pubkey = get_bridge_signing_pubkey(&self.unsigned_bridge_token.fingerprint);
self.unsigned_bridge_token.today <= today()
&& pubkey
.verify(
&bincode::serialize(&self.unsigned_bridge_token).unwrap(),
&self.sig,
)
.is_ok()
}
}