positive_report.rs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // For Lox-related code where points are uppercase and scalars are lowercase
  2. #![allow(non_snake_case)]
  3. // TODO: Make SerializableBridgeToken, check its fields while deserializing,
  4. // check that its fields match the report's fields while deserializing a report
  5. use crate::{get_date, CONFIG, COUNTRY_CODES};
  6. use curve25519_dalek::Scalar;
  7. use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
  8. use lox_library::{cred::Lox, proto::positive_report as lox_pr, IssuerPubKey};
  9. use serde::{Deserialize, Serialize};
  10. use sha1::{Digest, Sha1};
  11. use std::option::Option;
  12. #[derive(Debug)]
  13. pub enum PositiveReportError {
  14. DateInFuture,
  15. FailedToDeserialize, // couldn't deserialize to SerializablePositiveReport
  16. InvalidCountryCode,
  17. MissingBridgeToken,
  18. MissingCountryCode,
  19. }
  20. /// A report that the user was able to connect to the bridge
  21. pub struct PositiveReport {
  22. /// hashed fingerprint (SHA-1 hash of 20-byte bridge ID)
  23. pub fingerprint: [u8; 20],
  24. /// token from the bridge indicating it was reached
  25. bridge_token: Option<BridgeToken>,
  26. // proof of Lox cred with level >= 3 and this bridge
  27. lox_proof: lox_pr::Request,
  28. /// user's country code, may be an empty string
  29. pub country: String,
  30. /// today's Julian date
  31. pub date: u32,
  32. }
  33. impl PositiveReport {
  34. pub fn new(
  35. bridge_id: [u8; 20],
  36. bridge_token: Option<BridgeToken>,
  37. lox_proof: lox_pr::Request,
  38. country: String,
  39. ) -> Self {
  40. if CONFIG.require_bridge_token && bridge_token.is_none() {
  41. panic!("Bridge tokens are required for positive reports.");
  42. }
  43. let mut hasher = Sha1::new();
  44. hasher.update(bridge_id);
  45. let fingerprint: [u8; 20] = hasher.finalize().into();
  46. let date = get_date();
  47. Self {
  48. fingerprint,
  49. bridge_token,
  50. lox_proof,
  51. country,
  52. date,
  53. }
  54. }
  55. pub fn from_lox_credential(
  56. bridge_id: [u8; 20],
  57. bridge_token: Option<BridgeToken>,
  58. lox_cred: &Lox,
  59. lox_pub: &IssuerPubKey,
  60. country: String,
  61. ) -> Self {
  62. let lox_proof = lox_pr::request(lox_cred, lox_pub).unwrap();
  63. PositiveReport::new(bridge_id, bridge_token, lox_proof, country)
  64. }
  65. /// Convert report to a serializable version
  66. pub fn to_serializable_report(self) -> SerializablePositiveReport {
  67. SerializablePositiveReport {
  68. fingerprint: self.fingerprint,
  69. bridge_token: self.bridge_token,
  70. lox_proof: self.lox_proof,
  71. country: self.country,
  72. date: self.date,
  73. }
  74. }
  75. /// Serializes the report, eliding the underlying process
  76. pub fn to_json(self) -> String {
  77. serde_json::to_string(&self.to_serializable_report()).unwrap()
  78. }
  79. /// Deserializes the report, eliding the underlying process
  80. pub fn from_json(str: String) -> Result<Self, PositiveReportError> {
  81. match serde_json::from_str::<SerializablePositiveReport>(&str) {
  82. Ok(v) => v.to_report(),
  83. Err(_) => Err(PositiveReportError::FailedToDeserialize),
  84. }
  85. }
  86. /// Verify everything except the Lox proof.
  87. /// Parameters:
  88. /// - The bucket ID for the bucket containing this bridge
  89. /// - The bridge verifying key for this bridge
  90. /// These parameters are assumed to be correct and are NOT checked against
  91. /// the fingerprint listed in the report.
  92. pub fn verify_excluding_lox_proof(
  93. self,
  94. bucket: Scalar,
  95. bridge_key: Option<VerifyingKey>,
  96. ) -> bool {
  97. // Verify bridge token
  98. if CONFIG.require_bridge_token {
  99. let bridge_token = self.bridge_token.unwrap();
  100. if bridge_key.is_none() {
  101. return false;
  102. }
  103. if bridge_key
  104. .unwrap()
  105. .verify(
  106. &bincode::serialize(&bridge_token.unsigned_bridge_token).unwrap(),
  107. &bridge_token.sig,
  108. )
  109. .is_err()
  110. {
  111. return false;
  112. }
  113. }
  114. // Verify knowledge of bucket ID
  115. let H = self.lox_proof.H;
  116. let BP = self.lox_proof.BP;
  117. if bucket * H != BP {
  118. return false;
  119. }
  120. true
  121. }
  122. }
  123. /// (De)serializable positive report object which must be consumed by the
  124. /// checking function before it can be used
  125. #[derive(Deserialize, Serialize)]
  126. pub struct SerializablePositiveReport {
  127. pub fingerprint: [u8; 20],
  128. bridge_token: Option<BridgeToken>,
  129. lox_proof: lox_pr::Request,
  130. pub country: String,
  131. pub date: u32,
  132. }
  133. impl SerializablePositiveReport {
  134. pub fn to_report(self) -> Result<PositiveReport, PositiveReportError> {
  135. if CONFIG.require_bridge_token && self.bridge_token.is_none() {
  136. return Err(PositiveReportError::MissingBridgeToken);
  137. }
  138. if self.country == "" {
  139. return Err(PositiveReportError::MissingCountryCode);
  140. }
  141. if !COUNTRY_CODES.contains(self.country.as_str()) {
  142. return Err(PositiveReportError::InvalidCountryCode);
  143. }
  144. if self.date > get_date().into() {
  145. return Err(PositiveReportError::DateInFuture);
  146. }
  147. Ok(PositiveReport {
  148. fingerprint: self.fingerprint,
  149. bridge_token: self.bridge_token,
  150. lox_proof: self.lox_proof,
  151. country: self.country,
  152. date: self.date,
  153. })
  154. }
  155. }
  156. /// An unsigned token which indicates that the bridge was reached
  157. #[derive(Serialize, Deserialize)]
  158. pub struct UnsignedBridgeToken {
  159. /// hashed fingerprint (SHA-1 hash of 20-byte bridge ID)
  160. pub fingerprint: [u8; 20],
  161. /// client's country code
  162. pub country: String,
  163. /// today's Julian date
  164. pub date: u32,
  165. }
  166. impl UnsignedBridgeToken {
  167. pub fn new(bridge_id: [u8; 20], country: String) -> Self {
  168. let mut hasher = Sha1::new();
  169. hasher.update(bridge_id);
  170. let fingerprint: [u8; 20] = hasher.finalize().into();
  171. let date = get_date();
  172. Self {
  173. fingerprint,
  174. country,
  175. date,
  176. }
  177. }
  178. }
  179. /// A signed token which indicates that the bridge was reached
  180. #[derive(Serialize, Deserialize)]
  181. pub struct BridgeToken {
  182. /// the unsigned version of this token
  183. pub unsigned_bridge_token: UnsignedBridgeToken,
  184. /// signature from bridge's ed25519 key
  185. pub sig: Signature,
  186. }
  187. impl BridgeToken {
  188. pub fn new(unsigned_bridge_token: UnsignedBridgeToken, keypair: SigningKey) -> Self {
  189. let sig = keypair.sign(&bincode::serialize(&unsigned_bridge_token).unwrap());
  190. Self {
  191. unsigned_bridge_token,
  192. sig,
  193. }
  194. }
  195. }