Browse Source

Check bridge token fields when deserializing

Vecna 1 year ago
parent
commit
98fe935d7a
1 changed files with 117 additions and 12 deletions
  1. 117 12
      src/positive_report.rs

+ 117 - 12
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<BridgeToken>,
+    bridge_token: Option<SerializableBridgeToken>,
     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<PositiveReport, PositiveReportError> {
+        // 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<u8> {
+        bincode::serialize(&self.to_serializable_unsigned_bridge_token()).unwrap()
+    }
+
+    /// Deserializes the token, eliding the underlying process
+    pub fn from_bincode(vec: Vec<u8>) -> Result<Self, PositiveReportError> {
+        match bincode::deserialize::<SerializableUnsignedBridgeToken>(&vec[..]) {
+            Ok(v) => v.to_unsigned_bridge_token(),
+            Err(_) => Err(PositiveReportError::FailedToDeserialize),
+        }
+    }
 }
 
-/// A signed token which indicates that the bridge was reached
+/// (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<UnsignedBridgeToken, PositiveReportError> {
+        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
 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<Self, PositiveReportError> {
+        match serde_json::from_str::<SerializableBridgeToken>(&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<BridgeToken, PositiveReportError> {
+        let unsigned_bridge_token = self.unsigned_bridge_token.to_unsigned_bridge_token()?;
+        Ok(BridgeToken {
+            unsigned_bridge_token: unsigned_bridge_token,
+            sig: self.sig,
+        })
+    }
 }