diff --git a/src/positive_report.rs b/src/positive_report.rs index 6134036..ae01ad5 100644 --- a/src/positive_report.rs +++ b/src/positive_report.rs @@ -1,9 +1,6 @@ // For Lox-related code where points are uppercase and scalars are lowercase #![allow(non_snake_case)] -// TODO: Make SerializableBridgeToken, check its fields while deserializing, -// check that its fields match the report's fields while deserializing a report - use crate::{get_date, CONFIG, COUNTRY_CODES}; use curve25519_dalek::Scalar; @@ -17,6 +14,7 @@ use std::option::Option; pub enum PositiveReportError { DateInFuture, FailedToDeserialize, // couldn't deserialize to SerializablePositiveReport + InvalidBridgeToken, InvalidCountryCode, MissingBridgeToken, MissingCountryCode, @@ -72,9 +70,14 @@ impl PositiveReport { /// Convert report to a serializable version pub fn to_serializable_report(self) -> SerializablePositiveReport { + let bridge_token = if self.bridge_token.is_none() { + None + } else { + Some(self.bridge_token.unwrap().to_serializable_bridge_token()) + }; SerializablePositiveReport { fingerprint: self.fingerprint, - bridge_token: self.bridge_token, + bridge_token: bridge_token, lox_proof: self.lox_proof, country: self.country, date: self.date, @@ -97,7 +100,7 @@ impl PositiveReport { /// Verify everything except the Lox proof. /// Parameters: /// - The bucket ID for the bucket containing this bridge - /// - The bridge verifying key for this bridge + /// - The bridge verifying key for this bridge (if bridge token is required) /// These parameters are assumed to be correct and are NOT checked against /// the fingerprint listed in the report. pub fn verify_excluding_lox_proof( @@ -114,7 +117,7 @@ impl PositiveReport { if bridge_key .unwrap() .verify( - &bincode::serialize(&bridge_token.unsigned_bridge_token).unwrap(), + &bridge_token.unsigned_bridge_token.to_bincode(), &bridge_token.sig, ) .is_err() @@ -137,7 +140,7 @@ impl PositiveReport { #[derive(Deserialize, Serialize)] pub struct SerializablePositiveReport { pub fingerprint: [u8; 20], - bridge_token: Option, + bridge_token: Option, lox_proof: lox_pr::Request, pub country: String, pub date: u32, @@ -145,6 +148,7 @@ pub struct SerializablePositiveReport { impl SerializablePositiveReport { pub fn to_report(self) -> Result { + // Check that fields are valid if CONFIG.require_bridge_token && self.bridge_token.is_none() { return Err(PositiveReportError::MissingBridgeToken); } @@ -157,9 +161,23 @@ impl SerializablePositiveReport { if self.date > get_date().into() { return Err(PositiveReportError::DateInFuture); } + let bridge_token = if self.bridge_token.is_none() { + None + } else { + let bridge_token_unchecked = self.bridge_token.unwrap().to_bridge_token()?; + // Check that bridge token fields match report fields... + // The user may override the bridge's autodetected country code, + // so allow the country code to be different. + if self.fingerprint != bridge_token_unchecked.unsigned_bridge_token.fingerprint + || self.date != bridge_token_unchecked.unsigned_bridge_token.date + { + return Err(PositiveReportError::InvalidBridgeToken); + } + Some(bridge_token_unchecked) + }; Ok(PositiveReport { fingerprint: self.fingerprint, - bridge_token: self.bridge_token, + bridge_token: bridge_token, lox_proof: self.lox_proof, country: self.country, date: self.date, @@ -168,7 +186,6 @@ impl SerializablePositiveReport { } /// 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], @@ -190,23 +207,111 @@ impl UnsignedBridgeToken { date, } } + + pub fn to_serializable_unsigned_bridge_token(self) -> SerializableUnsignedBridgeToken { + SerializableUnsignedBridgeToken { + fingerprint: self.fingerprint, + country: self.country, + date: self.date, + } + } + + /// Serializes the token, eliding the underlying process + pub fn to_bincode(self) -> Vec { + bincode::serialize(&self.to_serializable_unsigned_bridge_token()).unwrap() + } + + /// Deserializes the token, eliding the underlying process + pub fn from_bincode(vec: Vec) -> Result { + match bincode::deserialize::(&vec[..]) { + Ok(v) => v.to_unsigned_bridge_token(), + Err(_) => Err(PositiveReportError::FailedToDeserialize), + } + } +} + +/// (De)serializable unsigned bridge token object which must be consumed by the +/// checking function before it can be used +#[derive(Serialize, Deserialize)] +pub struct SerializableUnsignedBridgeToken { + pub fingerprint: [u8; 20], + pub country: String, + pub date: u32, +} + +impl SerializableUnsignedBridgeToken { + pub fn to_unsigned_bridge_token(self) -> Result { + if self.country == "" + || !COUNTRY_CODES.contains(self.country.as_str()) + || self.date > get_date().into() + { + return Err(PositiveReportError::InvalidBridgeToken); + } + Ok(UnsignedBridgeToken { + fingerprint: self.fingerprint, + country: self.country, + date: self.date, + }) + } } /// 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, + sig: Signature, } impl BridgeToken { pub fn new(unsigned_bridge_token: UnsignedBridgeToken, keypair: SigningKey) -> Self { - let sig = keypair.sign(&bincode::serialize(&unsigned_bridge_token).unwrap()); + let bin = unsigned_bridge_token.to_bincode(); + let sig = keypair.sign(&bin); + let unsigned_bridge_token = UnsignedBridgeToken::from_bincode(bin).unwrap(); Self { unsigned_bridge_token, sig, } } + + /// Convert bridge token to a serializable version + pub fn to_serializable_bridge_token(self) -> SerializableBridgeToken { + SerializableBridgeToken { + unsigned_bridge_token: self + .unsigned_bridge_token + .to_serializable_unsigned_bridge_token(), + sig: self.sig, + } + } + + /// Serializes the bridge token, eliding the underlying process + pub fn to_json(self) -> String { + serde_json::to_string(&self.to_serializable_bridge_token()).unwrap() + } + + /// Deserializes the bridge token, eliding the underlying process + pub fn from_json(str: String) -> Result { + match serde_json::from_str::(&str) { + Ok(v) => v.to_bridge_token(), + Err(_) => Err(PositiveReportError::InvalidBridgeToken), + } + } +} + +/// (De)serializable bridge token object which must be consumed by the +/// checking function before it can be used +#[derive(Serialize, Deserialize)] +pub struct SerializableBridgeToken { + pub unsigned_bridge_token: SerializableUnsignedBridgeToken, + sig: Signature, +} + +impl SerializableBridgeToken { + pub fn to_bridge_token(self) -> Result { + let unsigned_bridge_token = self.unsigned_bridge_token.to_unsigned_bridge_token()?; + Ok(BridgeToken { + unsigned_bridge_token: unsigned_bridge_token, + sig: self.sig, + }) + } }