1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
use chrono::Duration;
use serde::{Deserialize, Serialize};
use serde_json;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::prelude::*;
use std::sync::Arc;
use super::jwt::{
create_jwt_encoded, download_google_jwks, verify_access_token, JWKSetDTO, JWT_AUDIENCE_IDENTITY,
};
type Error = super::errors::FirebaseError;
#[derive(Default)]
pub(crate) struct Keys {
pub pub_key: BTreeMap<String, Arc<biscuit::jws::Secret>>,
pub secret: Option<Arc<biscuit::jws::Secret>>,
}
#[derive(Serialize, Deserialize, Default, Clone)]
pub struct Credentials {
pub project_id: String,
pub private_key_id: String,
pub private_key: String,
pub client_email: String,
pub client_id: String,
pub api_key: String,
#[serde(default, skip)]
pub(crate) keys: Keys,
}
impl Clone for Keys {
fn clone(&self) -> Self {
Self {
pub_key: Default::default(),
secret: None,
}
}
}
pub fn pem_to_der(pem_file_contents: &str) -> Vec<u8> {
use regex::Regex;
use rustc_serialize::base64::FromBase64;
const REGEX: &'static str = r"(-----BEGIN .*-----\n)((?:(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)*\n)+)(-----END .*-----)";
let re = Regex::new(REGEX).unwrap();
let contents_without_headers = re.replace(pem_file_contents, "$2");
let base64_body = contents_without_headers.replace("\n", "");
base64_body.from_base64().unwrap()
}
impl Credentials {
pub fn new(credentials_file_content: &str, jwks_files: &[&str]) -> Result<Credentials, Error> {
let mut credentials: Credentials = serde_json::from_str(credentials_file_content)?;
for jwks_file in jwks_files {
credentials.add_jwks_public_keys(serde_json::from_str(jwks_file)?);
}
credentials.compute_secret()?;
credentials.verify()?;
Ok(credentials)
}
pub fn verify(&self) -> Result<(), Error> {
let access_token = create_jwt_encoded(
&self,
Some(["admin"].iter()),
Duration::hours(1),
Some(self.client_id.clone()),
None,
JWT_AUDIENCE_IDENTITY,
)?;
verify_access_token(&self, &access_token)?.ok_or(Error::Generic(
"Verification failed. Credentials do not match the public keys",
))?;
Ok(())
}
pub fn from_file(credential_file: &str) -> Result<Self, Error> {
let mut f = File::open(credential_file)?;
let mut buffer = Vec::new();
f.read_to_end(&mut buffer)?;
let mut credentials: Credentials = serde_json::from_slice(buffer.as_slice())?;
credentials.compute_secret()?;
credentials.download_google_jwks()?;
Ok(credentials)
}
pub fn decode_secret(&self, kid: &str) -> Option<Arc<biscuit::jws::Secret>> {
self.keys.pub_key.get(kid).and_then(|f| Some(f.clone()))
}
pub fn add_jwks_public_keys(&mut self, jwkset: JWKSetDTO) {
for entry in jwkset.keys.iter() {
if !entry.headers.key_id.is_some() {
continue;
}
let key_id = entry.headers.key_id.as_ref().unwrap().to_owned();
self.keys
.pub_key
.insert(key_id, Arc::new(entry.ne.jws_public_key_secret()));
}
}
pub fn compute_secret(&mut self) -> Result<(), Error> {
use biscuit::jws::Secret;
use ring::signature;
let vec = pem_to_der(&self.private_key);
let key_pair = signature::RsaKeyPair::from_pkcs8(&vec)?;
self.keys.secret = Some(Arc::new(Secret::RsaKeyPair(Arc::new(key_pair))));
Ok(())
}
pub fn download_google_jwks(&mut self) -> Result<(), Error> {
if self.keys.pub_key.is_empty() {
let jwks = download_google_jwks(&self.client_email)?;
self.add_jwks_public_keys(jwks);
let jwks = download_google_jwks("securetoken@system.gserviceaccount.com")?;
self.add_jwks_public_keys(jwks);
}
Ok(())
}
}