1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4 
5 use origin_trial_token::{Token, TokenValidationError, Usage};
6 use std::ffi::c_void;
7 
8 #[repr(u8)]
9 pub enum OriginTrial {
10     TestTrial,
11 }
12 
13 impl OriginTrial {
from_str(s: &str) -> Option<Self>14     fn from_str(s: &str) -> Option<Self> {
15         return Some(match s {
16             "TestTrial" => Self::TestTrial,
17             _ => return None,
18         });
19     }
20 }
21 
22 #[repr(u8)]
23 pub enum OriginTrialResult {
24     Ok { trial: OriginTrial },
25     BufferTooSmall,
26     MismatchedPayloadSize { expected: usize, actual: usize },
27     InvalidSignature,
28     UnknownVersion,
29     UnsupportedThirdPartyToken,
30     UnexpectedUsageInNonThirdPartyToken,
31     MalformedPayload,
32     ExpiredToken,
33     UnknownTrial,
34     OriginMismatch,
35 }
36 
37 /// A struct that allows you to configure how validation on works, and pass
38 /// state to the signature verification.
39 #[repr(C)]
40 pub struct OriginTrialValidationParams {
41     /// Verify a given signature against the signed data.
42     pub verify_signature: extern "C" fn(
43         signature: *const u8,
44         signature_len: usize,
45         data: *const u8,
46         data_len: usize,
47         user_data: *mut c_void,
48     ) -> bool,
49 
50     /// Returns whether a given origin, which is passed as the first two
51     /// arguments, and guaranteed to be valid UTF-8, passes the validation for a
52     /// given invocation.
53     pub matches_origin: extern "C" fn(
54         origin: *const u8,
55         len: usize,
56         is_subdomain: bool,
57         is_third_party: bool,
58         is_usage_subset: bool,
59         user_data: *mut c_void,
60     ) -> bool,
61 
62     /// A pointer with user-supplied data that will be passed down to the
63     /// other functions in this method.
64     pub user_data: *mut c_void,
65 }
66 
67 #[no_mangle]
origin_trials_parse_and_validate_token( bytes: *const u8, len: usize, params: &OriginTrialValidationParams, ) -> OriginTrialResult68 pub unsafe extern "C" fn origin_trials_parse_and_validate_token(
69     bytes: *const u8,
70     len: usize,
71     params: &OriginTrialValidationParams,
72 ) -> OriginTrialResult {
73     let slice = std::slice::from_raw_parts(bytes, len);
74     let token = Token::from_buffer(slice, |signature, data| {
75         (params.verify_signature)(
76             signature.as_ptr(),
77             signature.len(),
78             data.as_ptr(),
79             data.len(),
80             params.user_data,
81         )
82     });
83 
84     let token = match token {
85         Ok(token) => token,
86         Err(e) => {
87             return match e {
88                 TokenValidationError::BufferTooSmall => OriginTrialResult::BufferTooSmall,
89                 TokenValidationError::MismatchedPayloadSize { expected, actual } => {
90                     OriginTrialResult::MismatchedPayloadSize { expected, actual }
91                 }
92                 TokenValidationError::InvalidSignature => OriginTrialResult::InvalidSignature,
93                 TokenValidationError::UnknownVersion => OriginTrialResult::UnknownVersion,
94                 TokenValidationError::UnsupportedThirdPartyToken => {
95                     OriginTrialResult::UnsupportedThirdPartyToken
96                 }
97                 TokenValidationError::UnexpectedUsageInNonThirdPartyToken => {
98                     OriginTrialResult::UnexpectedUsageInNonThirdPartyToken
99                 }
100                 TokenValidationError::MalformedPayload(..) => OriginTrialResult::MalformedPayload,
101             }
102         }
103     };
104 
105     if token.is_expired() {
106         return OriginTrialResult::ExpiredToken;
107     }
108 
109     let trial = match OriginTrial::from_str(token.feature()) {
110         Some(t) => t,
111         None => return OriginTrialResult::UnknownTrial,
112     };
113 
114     let is_usage_subset = match token.usage {
115         Usage::None => false,
116         Usage::Subset => true,
117     };
118 
119     if !(params.matches_origin)(
120         token.origin.as_ptr(),
121         token.origin.len(),
122         token.is_subdomain,
123         token.is_third_party,
124         is_usage_subset,
125         params.user_data,
126     ) {
127         return OriginTrialResult::OriginMismatch;
128     }
129 
130     OriginTrialResult::Ok { trial }
131 }
132