Skip to main content

rustbac_core/services/
time_synchronization.rs

1use crate::apdu::UnconfirmedRequestHeader;
2use crate::encoding::{
3    reader::Reader,
4    tag::{AppTag, Tag},
5    writer::Writer,
6};
7use crate::types::{Date, Time};
8use crate::{DecodeError, EncodeError};
9
10pub const SERVICE_TIME_SYNCHRONIZATION: u8 = 0x06;
11pub const SERVICE_UTC_TIME_SYNCHRONIZATION: u8 = 0x09;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct TimeSynchronizationRequest {
15    pub date: Date,
16    pub time: Time,
17    pub utc: bool,
18}
19
20impl TimeSynchronizationRequest {
21    pub const fn local(date: Date, time: Time) -> Self {
22        Self {
23            date,
24            time,
25            utc: false,
26        }
27    }
28
29    pub const fn utc(date: Date, time: Time) -> Self {
30        Self {
31            date,
32            time,
33            utc: true,
34        }
35    }
36
37    pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
38        let service_choice = if self.utc {
39            SERVICE_UTC_TIME_SYNCHRONIZATION
40        } else {
41            SERVICE_TIME_SYNCHRONIZATION
42        };
43        UnconfirmedRequestHeader { service_choice }.encode(w)?;
44        Tag::Application {
45            tag: AppTag::Date,
46            len: 4,
47        }
48        .encode(w)?;
49        w.write_all(&[
50            self.date.year_since_1900,
51            self.date.month,
52            self.date.day,
53            self.date.weekday,
54        ])?;
55        Tag::Application {
56            tag: AppTag::Time,
57            len: 4,
58        }
59        .encode(w)?;
60        w.write_all(&[
61            self.time.hour,
62            self.time.minute,
63            self.time.second,
64            self.time.hundredths,
65        ])?;
66        Ok(())
67    }
68
69    pub fn decode_after_header(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
70        let date = match Tag::decode(r)? {
71            Tag::Application {
72                tag: AppTag::Date,
73                len: 4,
74            } => {
75                let bytes = r.read_exact(4)?;
76                Date {
77                    year_since_1900: bytes[0],
78                    month: bytes[1],
79                    day: bytes[2],
80                    weekday: bytes[3],
81                }
82            }
83            _ => return Err(DecodeError::InvalidTag),
84        };
85        let time = match Tag::decode(r)? {
86            Tag::Application {
87                tag: AppTag::Time,
88                len: 4,
89            } => {
90                let bytes = r.read_exact(4)?;
91                Time {
92                    hour: bytes[0],
93                    minute: bytes[1],
94                    second: bytes[2],
95                    hundredths: bytes[3],
96                }
97            }
98            _ => return Err(DecodeError::InvalidTag),
99        };
100        Ok(Self {
101            date,
102            time,
103            utc: false,
104        })
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::{
111        TimeSynchronizationRequest, SERVICE_TIME_SYNCHRONIZATION, SERVICE_UTC_TIME_SYNCHRONIZATION,
112    };
113    use crate::apdu::UnconfirmedRequestHeader;
114    use crate::encoding::{reader::Reader, writer::Writer};
115    use crate::types::{Date, Time};
116
117    #[test]
118    fn encode_local_time_sync_request() {
119        let req = TimeSynchronizationRequest::local(
120            Date {
121                year_since_1900: 126,
122                month: 2,
123                day: 7,
124                weekday: 6,
125            },
126            Time {
127                hour: 10,
128                minute: 11,
129                second: 12,
130                hundredths: 13,
131            },
132        );
133        let mut buf = [0u8; 64];
134        let mut w = Writer::new(&mut buf);
135        req.encode(&mut w).unwrap();
136        let mut r = Reader::new(w.as_written());
137        let hdr = UnconfirmedRequestHeader::decode(&mut r).unwrap();
138        assert_eq!(hdr.service_choice, SERVICE_TIME_SYNCHRONIZATION);
139    }
140
141    #[test]
142    fn encode_utc_time_sync_request() {
143        let req = TimeSynchronizationRequest::utc(
144            Date {
145                year_since_1900: 126,
146                month: 2,
147                day: 7,
148                weekday: 6,
149            },
150            Time {
151                hour: 14,
152                minute: 15,
153                second: 16,
154                hundredths: 17,
155            },
156        );
157        let mut buf = [0u8; 64];
158        let mut w = Writer::new(&mut buf);
159        req.encode(&mut w).unwrap();
160        let mut r = Reader::new(w.as_written());
161        let hdr = UnconfirmedRequestHeader::decode(&mut r).unwrap();
162        assert_eq!(hdr.service_choice, SERVICE_UTC_TIME_SYNCHRONIZATION);
163    }
164
165    #[test]
166    fn decode_local_time_sync_after_header() {
167        let req = TimeSynchronizationRequest::local(
168            Date {
169                year_since_1900: 126,
170                month: 2,
171                day: 7,
172                weekday: 6,
173            },
174            Time {
175                hour: 10,
176                minute: 11,
177                second: 12,
178                hundredths: 13,
179            },
180        );
181        let mut buf = [0u8; 64];
182        let mut w = Writer::new(&mut buf);
183        req.encode(&mut w).unwrap();
184        let mut r = Reader::new(w.as_written());
185        let _hdr = UnconfirmedRequestHeader::decode(&mut r).unwrap();
186        let decoded = TimeSynchronizationRequest::decode_after_header(&mut r).unwrap();
187        assert_eq!(decoded.date, req.date);
188        assert_eq!(decoded.time, req.time);
189    }
190}