1use rsa::RsaPrivateKey;
23use rsa::pkcs1::DecodeRsaPrivateKey;
24use rsa::pkcs1v15::SigningKey;
25use rsa::pkcs8::DecodePrivateKey;
26use rsa::sha2::Sha256;
27use rsa::signature::{SignatureEncoding, Signer};
28use serde_json::json;
29
30use crate::error::{Error, ErrorKind, Result};
31use crate::jwt::{b64url, now};
32
33const ISS: &str = "gaa";
34const AUD: &str = "gfw";
35const NAME: &str = "grabtaxi";
36pub const DEFAULT_VER: &str = "1.19.0.24";
38const TTL_SECS: i64 = 600;
40
41#[derive(Debug, Clone)]
44pub struct DeviceFields {
45 pub br_id: String,
46 pub br_id_v2: String,
47 pub br_uid: String,
48 pub suspicious: bool,
49}
50
51impl DeviceFields {
52 pub fn random() -> Self {
54 Self {
55 br_id: format!("{}y6r", hex(17)),
56 br_id_v2: format!("{}y6r", hex(17)),
57 br_uid: uuid_v4(),
58 suspicious: false,
59 }
60 }
61}
62
63fn rand_bytes(n: usize) -> Vec<u8> {
64 let mut buf = vec![0u8; n];
65 if getrandom::fill(&mut buf).is_err() {
68 let seed = now() as u64 ^ (std::process::id() as u64);
69 for (i, b) in buf.iter_mut().enumerate() {
70 *b = (seed
71 .wrapping_mul(6364136223846793005)
72 .wrapping_add(i as u64)
73 >> 33) as u8;
74 }
75 }
76 buf
77}
78
79fn hex(n: usize) -> String {
80 rand_bytes(n.div_ceil(2))
81 .iter()
82 .map(|b| format!("{b:02x}"))
83 .collect::<String>()
84 .chars()
85 .take(n)
86 .collect()
87}
88
89fn uuid_v4() -> String {
90 let mut b = rand_bytes(16);
91 b[6] = (b[6] & 0x0f) | 0x40;
92 b[8] = (b[8] & 0x3f) | 0x80;
93 let h: Vec<String> = b.iter().map(|x| format!("{x:02x}")).collect();
94 format!(
95 "{}{}{}{}-{}{}-{}{}-{}{}-{}{}{}{}{}{}",
96 h[0],
97 h[1],
98 h[2],
99 h[3],
100 h[4],
101 h[5],
102 h[6],
103 h[7],
104 h[8],
105 h[9],
106 h[10],
107 h[11],
108 h[12],
109 h[13],
110 h[14],
111 h[15]
112 )
113}
114
115fn load_key(pem: &str) -> Result<RsaPrivateKey> {
116 RsaPrivateKey::from_pkcs1_pem(pem)
117 .or_else(|_| RsaPrivateKey::from_pkcs8_pem(pem))
118 .map_err(|e| {
119 Error::new(
120 ErrorKind::HydraExpired,
121 format!("could not read hydra signing key: {e}"),
122 )
123 })
124}
125
126pub fn mint_native(key_pem: &str, fields: &DeviceFields, ver: &str) -> Result<(String, i64)> {
129 let key = load_key(key_pem)?;
130 let signing = SigningKey::<Sha256>::new(key);
131
132 let iat = now();
133 let exp = iat + TTL_SECS;
134 let header = json!({"alg": "RS256", "typ": "JWT"});
135 let claims = json!({
136 "iss": ISS,
137 "aud": AUD,
138 "name": NAME,
139 "iat": iat,
140 "exp": exp,
141 "nbf": iat,
142 "ver": ver,
143 "brID": fields.br_id,
144 "sus": fields.suspicious,
145 "brIDv2": fields.br_id_v2,
146 "brUID": fields.br_uid,
147 });
148
149 let signing_input = format!(
150 "{}.{}",
151 b64url(&serde_json::to_vec(&header)?),
152 b64url(&serde_json::to_vec(&claims)?)
153 );
154 let sig = signing.sign(signing_input.as_bytes());
155 let token = format!("{signing_input}.{}", b64url(&sig.to_vec()));
156 Ok((token, exp))
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162 use rsa::pkcs1::EncodeRsaPrivateKey;
163 use rsa::pkcs1v15::VerifyingKey;
164 use rsa::signature::Verifier;
165
166 fn test_key() -> (String, RsaPrivateKey) {
168 let mut rng = rsa::rand_core::OsRng;
169 let key = RsaPrivateKey::new(&mut rng, 2048).expect("keygen");
170 let pem = key
171 .to_pkcs1_pem(rsa::pkcs1::LineEnding::LF)
172 .expect("pem")
173 .to_string();
174 (pem, key)
175 }
176
177 #[test]
178 fn mints_a_verifiable_rs256_jwt() -> Result<()> {
179 let (pem, key) = test_key();
180 let fields = DeviceFields::random();
181 let (token, exp) = mint_native(&pem, &fields, DEFAULT_VER)?;
182
183 let parts: Vec<&str> = token.split('.').collect();
184 assert_eq!(parts.len(), 3);
185
186 let claims = crate::jwt::payload(&token)?;
188 assert_eq!(claims["iss"], "gaa");
189 assert_eq!(claims["aud"], "gfw");
190 assert_eq!(claims["name"], "grabtaxi");
191 assert_eq!(claims["ver"], DEFAULT_VER);
192 assert_eq!(claims["sus"], false);
193 assert_eq!(exp - claims["iat"].as_i64().unwrap_or(0), 600);
194
195 let vk = VerifyingKey::<Sha256>::new(key.to_public_key());
197 let signing_input = format!("{}.{}", parts[0], parts[1]);
198 let sig_bytes =
199 base64::Engine::decode(&base64::engine::general_purpose::URL_SAFE_NO_PAD, parts[2])
200 .expect("sig b64");
201 let sig = rsa::pkcs1v15::Signature::try_from(sig_bytes.as_slice()).expect("sig");
202 assert!(vk.verify(signing_input.as_bytes(), &sig).is_ok());
203 Ok(())
204 }
205
206 #[test]
207 fn device_fields_look_right() {
208 let f = DeviceFields::random();
209 assert_eq!(f.br_id.len(), 20);
210 assert!(f.br_uid.contains('-'));
211 assert_eq!(f.br_uid.len(), 36);
212 }
213
214 #[test]
215 fn bad_key_is_hydra_error() {
216 let e = mint_native("not a pem", &DeviceFields::random(), DEFAULT_VER)
217 .expect_err("should error");
218 assert_eq!(e.kind, ErrorKind::HydraExpired);
219 }
220}