rust_rcs_core/security/gba/
mod.rs

1// Copyright 2023 宋昊文
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15extern crate base64;
16extern crate chrono;
17extern crate futures;
18extern crate quick_xml;
19extern crate ring;
20extern crate tokio;
21extern crate url;
22
23use std::cell::UnsafeCell;
24use std::sync::atomic::{AtomicU32, Ordering};
25use std::sync::Arc;
26use std::time::SystemTime;
27
28use base64::{engine::general_purpose, Engine as _};
29use chrono::{DateTime, FixedOffset};
30
31use data_encoding::HEXUPPER;
32use futures::io::{AsyncBufRead, AsyncReadExt};
33
34use quick_xml::events::Event;
35use quick_xml::reader::Reader;
36use ring::hmac;
37
38use tokio::sync::Semaphore;
39use tokio::time::{self, Duration, Instant};
40
41use url::{Host, Url};
42
43use crate::ffi::log::platform_log;
44use crate::http::request::{Request, GET};
45use crate::http::response::Response;
46use crate::http::{HttpClient, HttpConnectionHandle};
47
48use crate::internet::header::{self, Header};
49
50use crate::internet::syntax;
51use crate::security::aka::{self, AkaChallenge, AkaResponse, AsAkaAlgorithm};
52use crate::security::authentication::{AuthenticationMethod, AuthenticationMethods};
53use crate::security::SecurityContext;
54
55use crate::util::rand;
56use crate::util::raw_string::FromRawStr;
57
58use super::authentication::digest::{DigestAnswerParams, DigestCredentials};
59
60const LOG_TAG: &str = "gba";
61
62pub struct GbaContext {
63    impi: String,
64    bsf_url: String,
65    bsf_realm: String,
66
67    subscription_id: i32,
68
69    bootstrapped_context: UnsafeCell<Option<Arc<BootstrappedContext>>>,
70    bootstrap_sem: Semaphore,
71}
72
73impl GbaContext {
74    pub fn new(
75        impi: String,
76        bsf_url: String,
77        bsf_realm: String,
78        subscription_id: i32,
79    ) -> GbaContext {
80        platform_log(
81            LOG_TAG,
82            format!(
83                "init GbaContext with url: {}, realm: {}",
84                &bsf_url, &bsf_realm
85            ),
86        );
87
88        GbaContext {
89            impi,
90            bsf_url,
91            bsf_realm,
92
93            subscription_id,
94
95            bootstrapped_context: None.into(),
96            bootstrap_sem: Semaphore::new(1),
97        }
98    }
99
100    pub fn try_get_bootstrapped_context(&self) -> Option<Arc<BootstrappedContext>> {
101        match self.bootstrap_sem.try_acquire() {
102            Ok(_permit) => {
103                let cell;
104                unsafe {
105                    cell = &*self.bootstrapped_context.get();
106                }
107                if let Some(bootstrapped_context) = cell {
108                    if bootstrapped_context.expires > Instant::now() {
109                        return Some(Arc::clone(&bootstrapped_context));
110                    }
111                }
112                None
113            }
114            Err(_) => None,
115        }
116    }
117
118    pub async fn get_bootstrapped_context(
119        &self,
120        http_client: &HttpClient,
121    ) -> Result<Arc<BootstrappedContext>, ErrorKind> {
122        platform_log(LOG_TAG, "get_bootstrapped_context");
123        match time::timeout_at(
124            Instant::now() + Duration::from_secs(60),
125            self.bootstrap(http_client),
126        )
127        .await
128        {
129            Ok(context) => context,
130
131            Err(_) => Err(ErrorKind::TimeOut),
132        }
133    }
134
135    async fn bootstrap(
136        &self,
137        http_client: &HttpClient,
138    ) -> Result<Arc<BootstrappedContext>, ErrorKind> {
139        platform_log(LOG_TAG, "bootstrap");
140        match self.bootstrap_sem.acquire().await {
141            Ok(_permit) => {
142                platform_log(LOG_TAG, "permit acquired");
143
144                let cell;
145                unsafe {
146                    cell = &*self.bootstrapped_context.get();
147                }
148
149                if let Some(bootstrapped_context) = cell {
150                    if bootstrapped_context.expires > Instant::now() {
151                        return Ok(Arc::clone(&bootstrapped_context));
152                    }
153                }
154
155                let bsf_url = format!("http://{}/", &self.bsf_url);
156
157                if let Ok(url) = Url::parse(&bsf_url) {
158                    match http_client.connect(&url, false).await {
159                        Ok(conn) => {
160                            let mut digest_answer = DigestAnswerParams {
161                                realm: self.bsf_realm.as_bytes().to_vec(),
162                                algorithm: None,
163
164                                username: self.impi.as_bytes().to_vec(),
165                                uri: b"".to_vec(),
166
167                                challenge: None,
168
169                                credentials: None,
170                            };
171
172                            if let Ok(authorization) =
173                                digest_answer.make_authorization_header(None, true, true)
174                            {
175                                let (resp, resp_stream) =
176                                    send_ub_message(&conn, &url, authorization).await?;
177
178                                platform_log(LOG_TAG, format!("ub status {}", resp.status_code));
179
180                                match decode_ub_resp(
181                                    &mut digest_answer,
182                                    resp,
183                                    resp_stream,
184                                    self.impi.as_bytes(),
185                                    b"\"/\"",
186                                    self.subscription_id,
187                                )
188                                .await
189                                {
190                                    UbResult::Res(authorization, rand, mut ck, mut ik) => {
191                                        let (resp, resp_stream) =
192                                            send_ub_message(&conn, &url, authorization).await?;
193
194                                        platform_log(
195                                            LOG_TAG,
196                                            format!("ub status {}", resp.status_code),
197                                        );
198
199                                        match decode_ub_resp(
200                                            &mut digest_answer,
201                                            resp,
202                                            resp_stream,
203                                            self.impi.as_bytes(),
204                                            b"\"/\"",
205                                            self.subscription_id,
206                                        )
207                                        .await
208                                        {
209                                            UbResult::Bootstrapped(b_tid, expiry) => {
210                                                ck.append(&mut ik);
211
212                                                let bootstrapped_context = BootstrappedContext {
213                                                    impi: self.impi.clone(),
214                                                    rand,
215                                                    ks: ck,
216                                                    b_tid,
217                                                    expires: expiry,
218                                                    used_count: AtomicU32::new(0),
219                                                };
220
221                                                let bootstrapped_context =
222                                                    Arc::new(bootstrapped_context);
223
224                                                let cell;
225                                                unsafe {
226                                                    cell = &mut *self.bootstrapped_context.get();
227                                                }
228
229                                                cell.replace(Arc::clone(&bootstrapped_context));
230
231                                                return Ok(bootstrapped_context);
232                                            }
233
234                                            _ => {}
235                                        }
236                                    }
237
238                                    UbResult::Auts(authorization) => {
239                                        platform_log(LOG_TAG, "on AUTS response");
240
241                                        let (resp, resp_stream) =
242                                            send_ub_message(&conn, &url, authorization).await?;
243
244                                        platform_log(
245                                            LOG_TAG,
246                                            format!("ub status {}", resp.status_code),
247                                        );
248
249                                        match decode_ub_resp(
250                                            &mut digest_answer,
251                                            resp,
252                                            resp_stream,
253                                            self.impi.as_bytes(),
254                                            b"\"/\"",
255                                            self.subscription_id,
256                                        )
257                                        .await
258                                        {
259                                            UbResult::Res(authorization, rand, mut ck, mut ik) => {
260                                                let (resp, resp_stream) =
261                                                    send_ub_message(&conn, &url, authorization)
262                                                        .await?;
263
264                                                platform_log(
265                                                    LOG_TAG,
266                                                    format!("ub status {}", resp.status_code),
267                                                );
268
269                                                match decode_ub_resp(
270                                                    &mut digest_answer,
271                                                    resp,
272                                                    resp_stream,
273                                                    self.impi.as_bytes(),
274                                                    b"\"/\"",
275                                                    self.subscription_id,
276                                                )
277                                                .await
278                                                {
279                                                    UbResult::Bootstrapped(b_tid, expiry) => {
280                                                        ck.append(&mut ik);
281
282                                                        let bootstrapped_context =
283                                                            BootstrappedContext {
284                                                                impi: self.impi.clone(),
285                                                                rand,
286                                                                ks: ck,
287                                                                b_tid,
288                                                                expires: expiry,
289                                                                used_count: AtomicU32::new(0),
290                                                            };
291
292                                                        let bootstrapped_context =
293                                                            Arc::new(bootstrapped_context);
294
295                                                        let cell;
296                                                        unsafe {
297                                                            cell = &mut *self
298                                                                .bootstrapped_context
299                                                                .get();
300                                                        }
301
302                                                        cell.replace(Arc::clone(
303                                                            &bootstrapped_context,
304                                                        ));
305
306                                                        return Ok(bootstrapped_context);
307                                                    }
308
309                                                    _ => {}
310                                                }
311                                            }
312
313                                            _ => {}
314                                        }
315                                    }
316
317                                    _ => {}
318                                }
319                            }
320
321                            Err(ErrorKind::Authentication)
322                        }
323
324                        Err(e) => {
325                            platform_log(LOG_TAG, format!("http error {:?}", e));
326
327                            Err(ErrorKind::ConnectionClosed)
328                        }
329                    }
330                } else {
331                    Err(ErrorKind::Http)
332                }
333            }
334
335            Err(_) => Err(ErrorKind::Closed),
336        }
337    }
338}
339
340unsafe impl Send for GbaContext {}
341unsafe impl Sync for GbaContext {}
342
343async fn send_ub_message(
344    connection: &HttpConnectionHandle,
345    url: &Url,
346    authorization: Vec<u8>,
347) -> Result<(Response, Option<Box<dyn AsyncBufRead + Send + Unpin>>), ErrorKind> {
348    let host = url.host_str().unwrap();
349
350    let host = match url.host() {
351        Some(Host::Domain(domain)) => String::from(domain),
352
353        Some(Host::Ipv4(ip)) => ip.to_string(),
354
355        Some(Host::Ipv6(ip)) => ip.to_string(),
356
357        None => String::from(host),
358    };
359
360    let mut req = Request::new_with_default_headers(GET, &host, "/", None);
361
362    req.headers
363        .push(Header::new(b"Authorization", authorization));
364
365    match connection.send(req, |_| {}).await {
366        Ok((resp, resp_stream)) => Ok((resp, resp_stream)),
367
368        Err(_) => Err(ErrorKind::Http),
369    }
370}
371
372enum UbResult {
373    Bootstrapped(String, Instant),
374    Res(Vec<u8>, [u8; 16], Vec<u8>, Vec<u8>),
375    Auts(Vec<u8>),
376    Error(ErrorKind),
377}
378
379async fn decode_ub_resp<'a>(
380    digest_answer: &'a mut DigestAnswerParams,
381    resp: Response,
382    resp_stream: Option<Box<dyn AsyncBufRead + Send + Unpin>>,
383    impi: &'a [u8],
384    uri: &'a [u8],
385    subscription_id: i32,
386) -> UbResult {
387    platform_log(LOG_TAG, "decode_ub_resp");
388    let status_code = resp.status_code;
389    if status_code == 200 {
390        if let Some(mut resp_stream) = resp_stream {
391            let mut resp_data = Vec::new();
392            if let Ok(_) = resp_stream.read_to_end(&mut resp_data).await {
393                if let Ok(resp_str) = std::str::from_utf8(&resp_data) {
394                    platform_log(LOG_TAG, format!("ub resp.string = {:?}", resp_str,));
395
396                    let mut xml_reader = Reader::from_str(resp_str);
397                    let mut buf = Vec::new();
398                    loop {
399                        match xml_reader.read_event_into(&mut buf) {
400                            Ok(Event::Start(ref e)) => {
401                                if e.name().as_ref() == b"BootstrappingInfo" {
402                                    let mut b_tid: Option<String> = None;
403                                    let mut expiry: Option<DateTime<FixedOffset>> = None;
404                                    let mut level = 1;
405                                    loop {
406                                        match xml_reader.read_event_into(&mut buf) {
407                                            Ok(Event::Start(ref e)) => {
408                                                if e.name().as_ref() == b"btid" {
409                                                    let mut level = 1;
410                                                    loop {
411                                                        match xml_reader.read_event_into(&mut buf) {
412                                                            Ok(Event::Text(e)) => {
413                                                                if level == 1 {
414                                                                    if let Ok(t) = e.unescape() {
415                                                                        b_tid.replace(
416                                                                            t.into_owned(),
417                                                                        );
418                                                                    }
419                                                                }
420                                                            }
421                                                            Ok(Event::Start(_)) => level += 1,
422                                                            Ok(Event::End(_)) => {
423                                                                level -= 1;
424                                                                if level == 0 {
425                                                                    break;
426                                                                }
427                                                            }
428                                                            _ => {}
429                                                        }
430                                                    }
431                                                    buf.clear();
432                                                } else if e.name().as_ref() == b"lifetime" {
433                                                    let mut level = 1;
434                                                    loop {
435                                                        match xml_reader.read_event_into(&mut buf) {
436                                                            Ok(Event::Text(e)) => {
437                                                                if level == 1 {
438                                                                    if let Ok(t) = e.unescape() {
439                                                                        if let Ok(lifetime) =
440                                                                            DateTime::parse_from_rfc3339(
441                                                                                &t,
442                                                                            )
443                                                                        {
444                                                                            expiry.replace(lifetime);
445                                                                        }
446                                                                    }
447                                                                }
448                                                            }
449                                                            Ok(Event::Start(_)) => level += 1,
450                                                            Ok(Event::End(_)) => {
451                                                                level -= 1;
452                                                                if level == 0 {
453                                                                    break;
454                                                                }
455                                                            }
456                                                            _ => {}
457                                                        }
458                                                    }
459                                                    buf.clear();
460                                                }
461                                            }
462                                            Ok(Event::End(ref e)) => {
463                                                level -= 1;
464                                                if level == 0 {
465                                                    break;
466                                                }
467                                            }
468                                            Ok(Event::Eof) => break,
469                                            _ => {}
470                                        }
471                                    }
472
473                                    buf.clear();
474
475                                    if let (Some(b_tid), Some(expiry)) = (b_tid, expiry) {
476                                        let expiry = SystemTime::UNIX_EPOCH
477                                            + std::time::Duration::from_secs(
478                                                expiry.timestamp() as u64
479                                            );
480                                        if let Ok(timeout) =
481                                            expiry.duration_since(SystemTime::now())
482                                        {
483                                            let expiry = std::time::Instant::now() + timeout;
484                                            let expiry = Instant::from_std(expiry);
485                                            return UbResult::Bootstrapped(b_tid, expiry);
486                                        }
487                                    }
488                                }
489                            }
490                            Ok(Event::Eof) => break,
491                            _ => {}
492                        }
493                    }
494                }
495            }
496        }
497    } else if status_code == 401 {
498        if let Some(www_authenticate_header) =
499            header::search(&resp.headers, b"WWW-Authenticate", false)
500        {
501            let www_authenticate_header_value = www_authenticate_header.get_value();
502            for method in AuthenticationMethods::new(www_authenticate_header_value) {
503                if let AuthenticationMethod::Digest(challenge_params) = method {
504                    if let Ok(algorithm) = challenge_params.algorithm.as_aka_algorithm() {
505                        if let Ok(aka_challenge) =
506                            AkaChallenge::from_raw_str(challenge_params.nonce)
507                        {
508                            digest_answer.challenge = Some(challenge_params.to_challenge());
509
510                            // to-do: can we really do this ?
511                            if digest_answer.uri.len() == 0 {
512                                digest_answer.uri = b"/".to_vec();
513                            }
514
515                            if let Ok(response) =
516                                aka::aka_do_challenge(&aka_challenge, subscription_id)
517                            {
518                                match response {
519                                    AkaResponse::Successful(res, Some((ck, ik))) => {
520                                        let cnonce = rand::create_raw_alpha_numeric_string(16);
521                                        digest_answer.credentials = Some(DigestCredentials {
522                                            password: res,
523                                            client_data: Some(b"GET".to_vec()),
524                                            client_nonce: Some((cnonce, 1)),
525                                            entity_digest: None,
526                                            extra_params: Vec::new(),
527                                        });
528                                        if let Ok(authorization) = digest_answer
529                                            .make_authorization_header(
530                                                Some(algorithm.algorithm),
531                                                false,
532                                                false,
533                                            )
534                                        {
535                                            return UbResult::Res(
536                                                authorization,
537                                                aka_challenge.rand,
538                                                ck,
539                                                ik,
540                                            );
541                                        }
542                                    }
543
544                                    AkaResponse::SyncFailure(auts) => {
545                                        let cnonce = rand::create_raw_alpha_numeric_string(16);
546                                        digest_answer.credentials = Some(DigestCredentials {
547                                            password: b"".to_vec(),
548                                            client_data: Some(b"GET".to_vec()),
549                                            client_nonce: Some((cnonce, 1)),
550                                            entity_digest: None,
551                                            extra_params: Vec::new(),
552                                        });
553                                        if let Ok(mut authorization) = digest_answer
554                                            .make_authorization_header(
555                                                Some(algorithm.algorithm),
556                                                false,
557                                                false,
558                                            )
559                                        {
560                                            authorization.extend_from_slice(b",auts=\"");
561                                            let encoded = general_purpose::STANDARD.encode(&auts);
562                                            let mut encoded = Vec::from(encoded);
563                                            authorization.append(&mut encoded);
564                                            authorization.extend_from_slice(b"\"");
565                                            return UbResult::Auts(authorization);
566                                        }
567                                    }
568
569                                    _ => {}
570                                }
571                            }
572                        }
573                    }
574                }
575            }
576
577            return UbResult::Error(ErrorKind::Authentication);
578        }
579    }
580
581    UbResult::Error(ErrorKind::Http)
582}
583
584pub struct BootstrappedContext {
585    pub impi: String,
586    pub rand: [u8; 16],
587    pub ks: Vec<u8>,
588    pub b_tid: String,
589    pub expires: Instant,
590    pub used_count: AtomicU32, // CAUTION: NC IS CURRENTLY ASSOCIATED WITH nonce USED IN BSF BOOTSTRAP UNDER ZTE 5G DEPLOYMENT
591}
592
593impl BootstrappedContext {
594    pub fn get_credential(
595        &self,
596        fqdn: &[u8],
597        cipher_id: Option<(u8, u8)>,
598    ) -> Result<GbaCredential, ErrorKind> {
599        let naf_id = gba_get_naf_id(fqdn, cipher_id);
600
601        let ks_naf = gba_me_ks_naf(&self.ks, &self.rand, self.impi.as_bytes(), &naf_id)?;
602        let ks_naf_hex = HEXUPPER.encode(&ks_naf);
603        platform_log(LOG_TAG, format!("ks_naf_hex: {}", &ks_naf_hex));
604
605        let encoded = general_purpose::STANDARD.encode(&ks_naf);
606        let encoded = Vec::from(encoded);
607
608        Ok(GbaCredential {
609            username: self.b_tid.as_bytes().to_vec(),
610            password: encoded,
611        })
612    }
613
614    pub fn increase_and_get_use_count(&self) -> u32 {
615        self.used_count.fetch_add(1, Ordering::SeqCst)
616    }
617}
618
619fn gba_kdf(ks: &[u8], p0: &[u8], p1: &[u8], p2: &[u8], p3: &[u8]) -> Result<Vec<u8>, ErrorKind> {
620    if p0.len() < 256 && p1.len() < 256 && p2.len() < 256 && p3.len() < 65536 {
621        let mut message = Vec::with_capacity(p0.len() + p1.len() + p2.len() + p3.len() + 9);
622
623        message.push(1);
624
625        message.extend_from_slice(p0);
626        message.push(0);
627        message.push(p0.len() as u8);
628
629        message.extend_from_slice(p1);
630        message.push(0);
631        message.push(p1.len() as u8);
632
633        let p2_len = (p2.len() as u16).to_be_bytes();
634
635        message.extend_from_slice(p2);
636        message.push(p2_len[0]);
637        message.push(p2_len[1]);
638
639        message.extend_from_slice(p3);
640
641        let p3_len = (p3.len() as u16).to_be_bytes();
642
643        message.push(p3_len[0]);
644        message.push(p3_len[1]);
645
646        let key = hmac::Key::new(hmac::HMAC_SHA256, ks);
647
648        let signature = hmac::sign(&key, &message);
649
650        return Ok(signature.as_ref().to_vec());
651    }
652
653    Err(ErrorKind::BadInput)
654}
655
656fn gba_me_ks_naf(ks: &[u8], rand: &[u8], impi: &[u8], naf_id: &[u8]) -> Result<Vec<u8>, ErrorKind> {
657    gba_kdf(ks, b"gba-me", rand, impi, naf_id)
658}
659
660fn gba_get_naf_id(fqdn: &[u8], cipher_id: Option<(u8, u8)>) -> Vec<u8> {
661    let mut naf_id = Vec::with_capacity(fqdn.len() + 5);
662
663    naf_id.extend_from_slice(fqdn);
664
665    if let Some((yy, zz)) = cipher_id {
666        naf_id.push(0x01);
667        naf_id.push(0x00);
668        naf_id.push(0x01);
669        naf_id.push(yy);
670        naf_id.push(zz);
671    } else {
672        naf_id.push(0x01);
673        naf_id.push(0x00);
674        naf_id.push(0x00);
675        naf_id.push(0x00);
676        naf_id.push(0x02);
677    }
678
679    naf_id
680}
681
682pub struct GbaCredential {
683    pub username: Vec<u8>,
684    pub password: Vec<u8>,
685}
686
687const UA_BOOTSTRAPPING_REQUIRED_INDICATION_AKA_ME: &[u8] = b"3GPP-bootstrapping";
688
689const UA_BOOTSTRAPPING_REQUIRED_INDICATION_AKA_UICC: &[u8] = b"3GPP-bootstrapping-uicc";
690
691const UA_BOOTSTRAPPING_REQUIRED_INDICATION_GBA_DIGEST: &[u8] = b"3GPP-bootstrapping-digest";
692
693pub enum GbaRealm<'a> {
694    Me(&'a [u8]),
695    Uicc(&'a [u8]),
696    Digest(&'a [u8]),
697}
698
699pub fn get_gba_realm<'a>(realm: &'a [u8]) -> Option<GbaRealm<'a>> {
700    if let Some(idx) = realm.iter().position(|c| *c == b'@') {
701        match &realm[..idx] {
702            UA_BOOTSTRAPPING_REQUIRED_INDICATION_AKA_ME => {
703                return Some(GbaRealm::Me(&realm[idx + 1..]));
704            }
705
706            UA_BOOTSTRAPPING_REQUIRED_INDICATION_AKA_UICC => {
707                return Some(GbaRealm::Uicc(&realm[idx + 1..]));
708            }
709
710            UA_BOOTSTRAPPING_REQUIRED_INDICATION_GBA_DIGEST => {
711                return Some(GbaRealm::Digest(&realm[idx + 1..]));
712            }
713
714            _ => {}
715        }
716    }
717
718    None
719}
720
721pub async fn try_process_401_response(
722    context: &GbaContext,
723    fqdn: &[u8],
724    cipher_id: Option<(u8, u8)>,
725    method: &[u8],
726    resource_uri: &[u8],
727    body_digest: Option<&[u8]>,
728    www_authenticate_header: &Header,
729    http_client: &HttpClient,
730    security_context: &SecurityContext,
731) -> Option<Result<DigestAnswerParams, ErrorKind>> {
732    platform_log(LOG_TAG, "on 401");
733
734    let www_authenticate_header_value = www_authenticate_header.get_value();
735    for auth_method in AuthenticationMethods::new(www_authenticate_header_value) {
736        if let AuthenticationMethod::Digest(challenge_params) = auth_method {
737            if let Some(gba_realm) = get_gba_realm(challenge_params.realm) {
738                platform_log(LOG_TAG, "is GBA bootstrap response");
739
740                match gba_realm {
741                    GbaRealm::Me(gba_fqdn) => {
742                        if gba_fqdn == fqdn {
743                            match context.get_bootstrapped_context(http_client).await {
744                                Ok(bootstrapped_context) => {
745                                    if let Ok(credential) =
746                                        bootstrapped_context.get_credential(fqdn, cipher_id)
747                                    {
748                                        let challenge = challenge_params.to_challenge();
749
750                                        let cnonce = rand::create_raw_alpha_numeric_string(16);
751                                        let nc = bootstrapped_context.increase_and_get_use_count();
752
753                                        let digest_answer = DigestAnswerParams {
754                                            realm: syntax::unquote(resource_uri).to_vec(),
755                                            algorithm: Some(challenge_params.algorithm.to_vec()),
756
757                                            username: credential.username,
758                                            uri: syntax::unquote(resource_uri).to_vec(),
759
760                                            challenge: Some(challenge),
761
762                                            credentials: Some(DigestCredentials {
763                                                password: credential.password,
764                                                client_data: Some(method.to_vec()),
765                                                client_nonce: Some((cnonce, nc)),
766                                                entity_digest: match body_digest {
767                                                    Some(body_digest) => Some(body_digest.to_vec()),
768                                                    None => None,
769                                                },
770                                                extra_params: Vec::new(),
771                                            }),
772                                        };
773
774                                        return Some(Ok(digest_answer));
775                                    }
776                                }
777
778                                Err(e) => {
779                                    return Some(Err(e));
780                                }
781                            }
782                        } else {
783                            platform_log(LOG_TAG, format!("bootstrap indicated fqdn {:?} which is different from {:?} we requested", std::str::from_utf8(gba_fqdn), std::str::from_utf8(fqdn)));
784                        }
785
786                        return Some(Err(ErrorKind::Authentication));
787                    }
788
789                    _ => {
790                        return Some(Err(ErrorKind::Authentication));
791                    }
792                }
793            }
794        }
795    }
796
797    None
798}
799
800pub enum ErrorKind {
801    TimeOut,
802    Closed,
803    ConnectionClosed,
804    Http,
805    Authentication,
806    BadInput,
807    Hmac,
808}