troll-patrol/src/lib.rs

225 lines
6.9 KiB
Rust
Raw Normal View History

2023-11-28 13:18:08 -05:00
use curve25519_dalek::scalar::Scalar;
use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
use lox_library::bridge_table::{BridgeLine, MAX_BRIDGES_PER_BUCKET};
2023-11-28 13:18:08 -05:00
use serde::{Deserialize, Serialize};
use sha1::{Digest, Sha1};
use sha3::Sha3_256;
// for generating ed25519 keys during initial development
use rand::rngs::OsRng;
2023-12-05 18:05:44 -05:00
// TODO: These should be loaded from config file
pub const REQUIRE_BRIDGE_TOKEN: bool = true;
2023-11-28 13:18:08 -05:00
/// 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()
}
/// Get bucket from hash of bucket ID, requires some oracle
pub fn get_bucket(beta_hash: &[u8; 32]) -> [BridgeLine; MAX_BRIDGES_PER_BUCKET] {
// TODO
// for now just return bucket of empty bridgelines
[
BridgeLine::default(),
BridgeLine::default(),
BridgeLine::default(),
]
}
2023-11-28 13:18:08 -05:00
/// 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] },
/// Hash of bucket ID for Lox user
HashOfBucket { hash: [u8; 32] },
2023-11-28 13:18:08 -05:00
}
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
}
ProofOfBridgeKnowledge::HashOfBucket { ref hash } => {
let bucket = get_bucket(&hash);
for bl in bucket {
let mut hasher = Sha1::new();
hasher.update(bl.uid_fingerprint.to_le_bytes());
if fingerprint == <[u8; 20]>::from(hasher.finalize()) {
return true;
}
}
false
}
2023-11-28 13:18:08 -05:00
}
}
}
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
2023-12-05 18:05:44 -05:00
pub bridge_token: Option<BridgeToken>,
2023-11-28 13:18:08 -05:00
// 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 {
2023-12-05 18:05:44 -05:00
pub fn new(bridge_id: [u8; 20], bridge_token: Option<BridgeToken>, country: String) -> Self {
2023-11-28 13:18:08 -05:00
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
2023-12-05 18:05:44 -05:00
self.today <= today()
&& (!REQUIRE_BRIDGE_TOKEN || {
if self.bridge_token.is_none() {
false
} else {
let bt = self.bridge_token.as_ref().unwrap();
self.today == bt.unsigned_bridge_token.today && bt.verify()
}
})
2023-11-28 13:18:08 -05:00
}
}
/// 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()
}
}