origin_trial_token/
lib.rs1pub const LATEST_VERSION: u8 = 3;
14
15#[repr(C)]
16pub struct RawToken {
17 version: u8,
18 signature: [u8; 64],
19 payload_length: [u8; 4],
20 payload: [u8; 0],
23}
24
25#[derive(Debug)]
26pub enum TokenValidationError {
27 BufferTooSmall,
28 MismatchedPayloadSize { expected: usize, actual: usize },
29 InvalidSignature,
30 UnknownVersion,
31 UnsupportedThirdPartyToken,
32 UnexpectedUsageInNonThirdPartyToken,
33 MalformedPayload(serde_json::Error),
34}
35
36impl RawToken {
37 const HEADER_SIZE: usize = std::mem::size_of::<Self>();
38
39 #[inline]
40 pub fn version(&self) -> u8 {
41 self.version
42 }
43
44 #[inline]
45 pub fn signature(&self) -> &[u8; 64] {
46 &self.signature
47 }
48
49 #[inline]
50 pub fn payload_length(&self) -> usize {
51 u32::from_be_bytes(self.payload_length) as usize
52 }
53
54 #[inline]
55 pub fn as_buffer(&self) -> &[u8] {
56 let buffer_size = Self::HEADER_SIZE + self.payload_length();
57 unsafe { std::slice::from_raw_parts(self as *const _ as *const u8, buffer_size) }
58 }
59
60 #[inline]
61 pub fn payload(&self) -> &[u8] {
62 let len = self.payload_length();
63 unsafe { std::slice::from_raw_parts(self.payload.as_ptr(), len) }
64 }
65
66 pub fn from_buffer<'a>(buffer: &'a [u8]) -> Result<&'a Self, TokenValidationError> {
68 if buffer.len() <= Self::HEADER_SIZE {
69 return Err(TokenValidationError::BufferTooSmall);
70 }
71 assert_eq!(
72 std::mem::align_of::<Self>(),
73 1,
74 "RawToken is a view over the buffer"
75 );
76 let raw_token = unsafe { &*(buffer.as_ptr() as *const Self) };
77 let payload = &buffer[Self::HEADER_SIZE..];
78 let expected = raw_token.payload_length();
79 let actual = payload.len();
80 if expected != actual {
81 return Err(TokenValidationError::MismatchedPayloadSize { expected, actual });
82 }
83 Ok(raw_token)
84 }
85
86 fn signature_data(&self) -> Vec<u8> {
88 Self::raw_signature_data(self.version, self.payload())
89 }
90
91 fn raw_signature_data(version: u8, payload: &[u8]) -> Vec<u8> {
93 let mut data = Vec::with_capacity(payload.len() + 5);
94 data.push(version);
95 data.extend((payload.len() as u32).to_be_bytes());
96 data.extend(payload);
97 data
98 }
99
100 pub fn verify(&self, verify_signature: impl FnOnce(&[u8; 64], &[u8]) -> bool) -> bool {
102 let signature_data = self.signature_data();
103 verify_signature(&self.signature, &signature_data)
104 }
105}
106
107#[derive(serde::Deserialize, serde::Serialize, Debug, Eq, PartialEq)]
108#[serde(rename_all = "camelCase")]
109pub enum Usage {
110 #[serde(rename = "")]
111 None,
112 Subset,
113}
114
115impl Usage {
116 fn is_none(&self) -> bool {
117 *self == Self::None
118 }
119}
120
121impl Default for Usage {
122 fn default() -> Self {
123 Self::None
124 }
125}
126
127fn is_false(t: &bool) -> bool {
128 *t == false
129}
130
131#[derive(serde::Deserialize, serde::Serialize, Debug, Eq, PartialEq)]
133#[serde(rename_all = "camelCase")]
134pub struct Token {
135 pub origin: String,
136 pub feature: String,
137 pub expiry: u64, #[serde(default, skip_serializing_if = "is_false")]
139 pub is_subdomain: bool,
140 #[serde(default, skip_serializing_if = "is_false")]
141 pub is_third_party: bool,
142 #[serde(default, skip_serializing_if = "Usage::is_none")]
143 pub usage: Usage,
144}
145
146impl Token {
147 #[inline]
148 pub fn origin(&self) -> &str {
149 &self.origin
150 }
151
152 #[inline]
153 pub fn feature(&self) -> &str {
154 &self.feature
155 }
156
157 #[inline]
158 pub fn expiry_since_unix_epoch(&self) -> std::time::Duration {
159 std::time::Duration::from_secs(self.expiry)
160 }
161
162 #[inline]
163 pub fn expiry_time(&self) -> Option<std::time::SystemTime> {
164 std::time::UNIX_EPOCH.checked_add(self.expiry_since_unix_epoch())
165 }
166
167 #[inline]
168 pub fn is_expired(&self) -> bool {
169 let now_duration = std::time::SystemTime::now()
170 .duration_since(std::time::UNIX_EPOCH)
171 .expect("System time before epoch?");
172 now_duration >= self.expiry_since_unix_epoch()
173 }
174
175 pub fn from_buffer(
178 buffer: &[u8],
179 verify_signature: impl FnOnce(&[u8; 64], &[u8]) -> bool,
180 ) -> Result<Self, TokenValidationError> {
181 Self::from_raw_token(RawToken::from_buffer(buffer)?, verify_signature)
182 }
183
184 pub fn from_raw_token(
186 token: &RawToken,
187 verify_signature: impl FnOnce(&[u8; 64], &[u8]) -> bool,
188 ) -> Result<Self, TokenValidationError> {
189 if !token.verify(verify_signature) {
190 return Err(TokenValidationError::InvalidSignature);
191 }
192 Self::from_raw_token_unverified(token)
193 }
194
195 pub fn from_raw_token_unverified(token: &RawToken) -> Result<Self, TokenValidationError> {
197 Self::from_payload(token.version, token.payload())
198 }
199
200 pub fn from_payload(version: u8, payload: &[u8]) -> Result<Self, TokenValidationError> {
202 if version != 2 && version != 3 {
203 assert_ne!(version, LATEST_VERSION);
204 return Err(TokenValidationError::UnknownVersion);
205 }
206
207 let token: Token = match serde_json::from_slice(payload) {
208 Ok(t) => t,
209 Err(e) => return Err(TokenValidationError::MalformedPayload(e)),
210 };
211
212 if token.is_third_party {
214 if version == 2 {
215 return Err(TokenValidationError::UnsupportedThirdPartyToken);
216 }
217 } else if !token.usage.is_none() {
218 return Err(TokenValidationError::UnexpectedUsageInNonThirdPartyToken);
219 }
220
221 Ok(token)
222 }
223
224 pub fn to_payload(&self) -> Vec<u8> {
226 serde_json::to_string(self)
227 .expect("Should always be able to turn a token into a payload")
228 .into_bytes()
229 }
230
231 pub fn to_signature_data(&self) -> Vec<u8> {
233 RawToken::raw_signature_data(LATEST_VERSION, &self.to_payload())
234 }
235
236 pub fn to_signed_token(&self, sign: impl FnOnce(&[u8]) -> [u8; 64]) -> Vec<u8> {
238 self.to_signed_token_with_payload(sign, &self.to_payload())
239 }
240
241 fn to_signed_token_with_payload(
245 &self,
246 sign: impl FnOnce(&[u8]) -> [u8; 64],
247 payload: &[u8],
248 ) -> Vec<u8> {
249 let signature_data_with_payload = RawToken::raw_signature_data(LATEST_VERSION, &payload);
250 let signature = sign(&signature_data_with_payload);
251
252 let mut buffer = Vec::with_capacity(1 + signature.len() + 4 + payload.len());
253 buffer.push(LATEST_VERSION);
254 buffer.extend(signature);
255 buffer.extend((payload.len() as u32).to_be_bytes());
256 buffer.extend(payload);
257
258 if cfg!(debug_assertions) {
259 let token = Self::from_buffer(&buffer, |_, _| true).expect("Creating malformed token?");
260 assert_eq!(self, &token, "Token differs after deserialization?");
261 }
262
263 buffer
264 }
265}
266
267#[cfg(test)]
268mod tests;