Define initial data structures
This commit is contained in:
parent
7f375eaca8
commit
e153d58fd2
13
Cargo.toml
13
Cargo.toml
|
@ -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"]}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue