rust_rcs_core/security/aka/
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;
16
17use base64::{engine::general_purpose, Engine as _};
18use data_encoding::HEXUPPER;
19use libc::{c_int, c_void, size_t};
20
21use crate::ffi::icc::IccChannel;
22
23use crate::ffi::log::platform_log;
24use crate::util::raw_string::{FromRawStr, StrFind};
25
26#[cfg(any(
27    all(feature = "android", target_os = "android"),
28    all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
29))]
30const PLATFORM_SUPPORT_DIRECT_AKA: bool = true;
31
32#[cfg(not(any(
33    all(feature = "android", target_os = "android"),
34    all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
35)))]
36const PLATFORM_SUPPORT_DIRECT_AKA: bool = false;
37
38const LOG_TAG: &str = "aka";
39
40#[cfg(any(
41    all(feature = "android", target_os = "android"),
42    all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
43))]
44extern "C" {
45    fn platform_perform_aka(
46        subscription_id: c_int,
47        in_data: *const c_void,
48        in_size: size_t,
49        out_size: *mut size_t,
50    ) -> *mut c_void;
51}
52
53#[cfg(any(
54    all(feature = "android", target_os = "android"),
55    all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
56))]
57fn perform_aka(challenge_data: &[u8], subscription_id: i32) -> Result<Vec<u8>, ErrorKind> {
58    let mut out_size: size_t = 0;
59    let out_data;
60
61    unsafe {
62        out_data = platform_perform_aka(
63            subscription_id,
64            challenge_data.as_ptr() as *const c_void,
65            challenge_data.len(),
66            &mut out_size,
67        );
68
69        if out_size <= 0 || out_data.is_null() {
70            return Err(ErrorKind::FFI);
71        }
72
73        let mut data: Vec<u8> = Vec::with_capacity(out_size);
74
75        std::ptr::copy_nonoverlapping(out_data as *const u8, data.as_mut_ptr(), out_size);
76
77        libc::free(out_data);
78
79        data.set_len(out_size);
80
81        return Ok(data);
82    }
83}
84
85#[cfg(not(any(
86    all(feature = "android", target_os = "android"),
87    all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
88)))]
89fn perform_aka(challenge_data: &[u8], subscription_id: i32) -> Result<Vec<u8>, ErrorKind> {
90    Err(ErrorKind::FFI)
91}
92
93pub struct AkaAlgorithm<'a> {
94    pub version: i16,
95    pub algorithm: &'a [u8],
96}
97
98pub trait AsAkaAlgorithm<'a> {
99    type Target;
100    type Err;
101    fn as_aka_algorithm(&'a self) -> Result<Self::Target, Self::Err>;
102}
103
104impl<'a> AsAkaAlgorithm<'a> for [u8] {
105    type Target = AkaAlgorithm<'a>;
106    type Err = ();
107    fn as_aka_algorithm(&'a self) -> Result<AkaAlgorithm<'a>, ()> {
108        let mut iter = self.into_iter();
109
110        if let Some(5) = iter.position(|c| *c == b'-') {
111            if self.start_with(b"AKAv1") {
112                return Ok(AkaAlgorithm {
113                    version: 1,
114                    algorithm: &self[6..],
115                });
116            }
117        }
118
119        Err(())
120    }
121}
122
123pub struct AkaChallenge {
124    pub rand: [u8; 16],
125    pub autn: [u8; 16],
126}
127
128impl FromRawStr for AkaChallenge {
129    type Err = ();
130    fn from_raw_str(s: &[u8]) -> Result<AkaChallenge, ()> {
131        if let Ok(s) = general_purpose::STANDARD.decode(s) {
132            if s.len() >= 32 {
133                let mut aka_challenge = AkaChallenge {
134                    rand: [0; 16],
135                    autn: [0; 16],
136                };
137
138                unsafe {
139                    std::ptr::copy_nonoverlapping(
140                        s[..16].as_ptr(),
141                        aka_challenge.rand.as_mut_ptr(),
142                        16,
143                    );
144                    std::ptr::copy_nonoverlapping(
145                        s[16..32].as_ptr(),
146                        aka_challenge.autn.as_mut_ptr(),
147                        16,
148                    );
149                }
150
151                return Ok(aka_challenge);
152            }
153        }
154
155        Err(())
156    }
157}
158
159pub enum AkaResponse {
160    Successful(Vec<u8>, Option<(Vec<u8>, Vec<u8>)>),
161    SyncFailure(Vec<u8>),
162}
163
164fn aka_decode_response(data: Vec<u8>) -> Result<AkaResponse, ErrorKind> {
165    if data.len() >= 2 {
166        let tag = data[0];
167        if tag == 0xDB {
168            let res_length = data[1] as usize;
169            platform_log(LOG_TAG, format!("res_length:{}", res_length));
170            if data.len() >= 2 + res_length {
171                let mut res = Vec::with_capacity(res_length);
172                res.extend_from_slice(&data[2..2 + res_length]);
173                platform_log(LOG_TAG, format!("res:{}", &HEXUPPER.encode(&res)));
174                if data.len() > 2 + res_length {
175                    let ck_length = data[2 + res_length] as usize;
176                    platform_log(LOG_TAG, format!("ck_length:{}", res_length));
177                    if data.len() >= 2 + res_length + 1 + ck_length {
178                        let mut ck = Vec::with_capacity(ck_length);
179                        ck.extend_from_slice(
180                            &data[2 + res_length + 1..2 + res_length + 1 + ck_length],
181                        );
182                        platform_log(LOG_TAG, format!("ck:{}", &HEXUPPER.encode(&ck)));
183                        if data.len() > 2 + res_length + 1 + ck_length {
184                            let ik_length = data[2 + res_length + 1 + ck_length] as usize;
185                            platform_log(LOG_TAG, format!("ik_length:{}", ik_length));
186                            if data.len() >= 2 + res_length + 1 + ck_length + 1 + ik_length {
187                                let mut ik = Vec::with_capacity(ik_length);
188                                ik.extend_from_slice(
189                                    &data[2 + res_length + 1 + ck_length + 1
190                                        ..2 + res_length + 1 + ck_length + 1 + ik_length],
191                                );
192                                platform_log(LOG_TAG, format!("ik:{}", &HEXUPPER.encode(&ik)));
193                                return Ok(AkaResponse::Successful(res, Some((ck, ik))));
194                            }
195                        }
196                    }
197                }
198
199                return Ok(AkaResponse::Successful(res, None));
200            }
201        } else if tag == 0xDC {
202            let auts_length = data[1] as usize;
203            platform_log(LOG_TAG, format!("auts_length:{}", auts_length));
204            if data.len() >= 2 + auts_length {
205                let mut auts = Vec::with_capacity(auts_length);
206                auts.extend_from_slice(&data[2..2 + auts_length]);
207                platform_log(LOG_TAG, format!("auts:{}", &HEXUPPER.encode(&auts)));
208                return Ok(AkaResponse::SyncFailure(auts));
209            }
210        }
211    }
212
213    Err(ErrorKind::BadFormat)
214}
215
216pub fn aka_do_challenge(
217    challenge: &AkaChallenge,
218    subscription_id: i32,
219) -> Result<AkaResponse, ErrorKind> {
220    let mut challenge_data: [u8; 34] = [0; 34];
221
222    challenge_data[0] = 16;
223    challenge_data[17] = 16;
224
225    unsafe {
226        std::ptr::copy_nonoverlapping(
227            challenge.rand.as_ptr(),
228            challenge_data[1..17].as_mut_ptr(),
229            16,
230        );
231        std::ptr::copy_nonoverlapping(
232            challenge.autn.as_ptr(),
233            challenge_data[18..].as_mut_ptr(),
234            16,
235        );
236    }
237
238    if PLATFORM_SUPPORT_DIRECT_AKA {
239        if let Ok(data) = perform_aka(&challenge_data, subscription_id) {
240            return aka_decode_response(data);
241        }
242    } else {
243        let aid_bytes: [u8; 7] = [0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02];
244
245        if let Some(channel) = IccChannel::new(&aid_bytes) {
246            let cla = 0x00;
247            let ins = 0x88;
248            let p1 = 0x00;
249            let p2 = 0x81;
250            let lc = 0x22;
251            let _le = 0x00;
252
253            // let mut command : [u8; 5 + 34 + 1]  = [0; 5 + 34 + 1];
254
255            // command[0] = cla;
256            // command[1] = ins;
257            // command[2] = p1;
258            // command[3] = p2;
259            // command[4] = lc;
260
261            // unsafe {
262            //     std::ptr::copy_nonoverlapping(challenge_data.as_ptr(), command[5..].as_mut_ptr(), 34);
263            // }
264
265            // command[39] = le;
266
267            if let Ok(data) = channel.icc_exchange_apdu(cla, ins, p1, p2, lc, &challenge_data) {
268                return aka_decode_response(data);
269            }
270        }
271    }
272
273    Err(ErrorKind::FFI)
274}
275
276pub enum ErrorKind {
277    BadFormat,
278    FFI,
279    UnknownParameter,
280}