Clean up enum definitions/shared behavior, add struct creation functions

This commit is contained in:
Vecna 2023-12-05 19:55:33 -05:00
parent 1f21eeeb53
commit 326d7e5560
1 changed files with 98 additions and 34 deletions

View File

@ -1,6 +1,7 @@
use curve25519_dalek::scalar::Scalar;
use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
use lox_library::bridge_table::{BridgeLine, MAX_BRIDGES_PER_BUCKET};
use lox_library::cred::Lox;
use serde::{Deserialize, Serialize};
use sha1::{Digest, Sha1};
use sha3::Sha3_256;
@ -51,38 +52,86 @@ pub fn get_bucket(beta_hash: &[u8; 32]) -> [BridgeLine; MAX_BRIDGES_PER_BUCKET]
#[derive(Serialize, Deserialize)]
pub enum ProofOfBridgeKnowledge {
/// Hash of bridge line as proof of knowledge of bridge line
HashOfBridgeLine { hash: [u8; 32] },
HashOfBridgeLine(HashOfBridgeLine),
/// Hash of bucket ID for Lox user
HashOfBucket { hash: [u8; 32] },
HashOfBucket(HashOfBucket),
}
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
pub fn verify(&self, bridge_fingerprint: [u8; 20]) -> bool {
// TODO: It seems like there ought to be a cleaner way to do this?
match self {
ProofOfBridgeKnowledge::HashOfBridgeLine(bl_hash) => bl_hash.verify(bridge_fingerprint),
ProofOfBridgeKnowledge::HashOfBucket(bucket_hash) => {
bucket_hash.verify(bridge_fingerprint)
}
}
}
}
pub trait Report {
fn verify(&self) -> bool;
/// Hash of bridge line to prove knowledge of that bridge
#[derive(PartialEq, Serialize, Deserialize)]
pub struct HashOfBridgeLine {
hash: [u8; 32],
}
impl HashOfBridgeLine {
pub fn new(bl: BridgeLine) -> Self {
let mut hasher = Sha3_256::new();
hasher.update(bincode::serialize(&bl).unwrap());
let hash: [u8; 32] = hasher.finalize().into();
Self { hash }
}
pub fn verify(&self, bridge_fingerprint: [u8; 20]) -> bool {
let bl = get_bridge_line(&bridge_fingerprint);
self == &HashOfBridgeLine::new(bl)
}
}
/// Hash of bucket ID to prove knowledge of bridges in that bucket
#[derive(PartialEq, Serialize, Deserialize)]
pub struct HashOfBucket {
hash: [u8; 32],
}
impl HashOfBucket {
pub fn new(bucket: Scalar) -> Self {
let mut hasher = Sha3_256::new();
hasher.update(bucket.to_bytes());
let hash: [u8; 32] = hasher.finalize().into();
Self { hash }
}
pub fn verify(&self, bridge_fingerprint: [u8; 20]) -> bool {
let bucket = get_bucket(&self.hash);
for bl in bucket {
let mut hasher = Sha1::new();
hasher.update(bl.uid_fingerprint.to_le_bytes());
if bridge_fingerprint == <[u8; 20]>::from(hasher.finalize()) {
return true;
}
}
false
}
}
/// Reports from users about whether or not their bridges are blocked
#[derive(Serialize, Deserialize)]
pub enum Report {
/// Negative report indicating user was unable to connect
NegativeUserReport(NegativeUserReport),
/// Positive report indicating user was able to connect
PositiveUserReport(PositiveUserReport),
}
impl Report {
fn verify(&self) -> bool {
match self {
Report::NegativeUserReport(report) => report.verify(),
Report::PositiveUserReport(report) => report.verify(),
}
}
}
/// A report that the user was unable to connect to the bridge
@ -91,7 +140,7 @@ 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,
bridge_pok: ProofOfBridgeKnowledge,
/// user's country code, may be an empty string
pub country: String,
/// today's Julian date
@ -99,14 +148,10 @@ pub struct NegativeUserReport {
}
impl NegativeUserReport {
pub fn new(bridge_id: [u8; 20], bucket: Scalar, country: String) -> Self {
pub fn new(bridge_id: [u8; 20], bridge_pok: ProofOfBridgeKnowledge, 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,
@ -115,9 +160,30 @@ impl NegativeUserReport {
today,
}
}
}
impl Report for NegativeUserReport {
pub fn from_bridgeline(
&self,
bridge_id: [u8; 20],
bridgeline: BridgeLine,
country: String,
) -> Self {
let bridge_pok =
ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(bridgeline));
NegativeUserReport::new(bridge_id, bridge_pok, country)
}
pub fn from_bucket(&self, bridge_id: [u8; 20], bucket: Scalar, country: String) -> Self {
let mut hasher = Sha3_256::new();
hasher.update(bucket.to_bytes());
let bucket_hash: [u8; 32] = hasher.finalize().into();
let bridge_pok = ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket { hash: bucket_hash });
NegativeUserReport::new(bridge_id, bridge_pok, country)
}
pub fn from_lox_credential(&self, bridge_id: [u8; 20], cred: Lox, country: String) -> Self {
self.from_bucket(bridge_id, cred.bucket, country)
}
fn verify(&self) -> bool {
// possibly include check that self.today is recent as well
self.today <= today() && self.bridge_pok.verify(self.fingerprint)
@ -130,7 +196,7 @@ 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: Option<BridgeToken>,
bridge_token: Option<BridgeToken>,
// TODO: proof of level, something involving credential show
/// user's country code, may be an empty string
pub country: String,
@ -151,9 +217,7 @@ impl PositiveUserReport {
today,
}
}
}
impl Report for PositiveUserReport {
fn verify(&self) -> bool {
// possibly include check that self.today is recent as well
self.today <= today()