1use crate::apdu::ConfirmedRequestHeader;
2use crate::encoding::{primitives::encode_ctx_unsigned, tag::Tag, writer::Writer};
3use crate::EncodeError;
4
5#[cfg(feature = "alloc")]
6extern crate alloc;
7#[cfg(feature = "alloc")]
8use alloc::vec::Vec;
9
10#[cfg(feature = "alloc")]
11use crate::encoding::{primitives::decode_unsigned, reader::Reader};
12#[cfg(feature = "alloc")]
13use crate::DecodeError;
14
15pub const SERVICE_CONFIRMED_PRIVATE_TRANSFER: u8 = 18;
16pub const SERVICE_UNCONFIRMED_PRIVATE_TRANSFER: u8 = 4;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct ConfirmedPrivateTransferRequest<'a> {
23 pub vendor_id: u32,
24 pub service_number: u32,
25 pub service_parameters: Option<&'a [u8]>,
26 pub invoke_id: u8,
27}
28
29impl<'a> ConfirmedPrivateTransferRequest<'a> {
30 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
31 ConfirmedRequestHeader {
32 segmented: false,
33 more_follows: false,
34 segmented_response_accepted: false,
35 max_segments: 0,
36 max_apdu: 5,
37 invoke_id: self.invoke_id,
38 sequence_number: None,
39 proposed_window_size: None,
40 service_choice: SERVICE_CONFIRMED_PRIVATE_TRANSFER,
41 }
42 .encode(w)?;
43
44 encode_ctx_unsigned(w, 0, self.vendor_id)?;
46 encode_ctx_unsigned(w, 1, self.service_number)?;
48 if let Some(params) = self.service_parameters {
50 Tag::Opening { tag_num: 2 }.encode(w)?;
51 w.write_all(params)?;
52 Tag::Closing { tag_num: 2 }.encode(w)?;
53 }
54 Ok(())
55 }
56}
57
58#[cfg(feature = "alloc")]
60#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct ConfirmedPrivateTransferAck {
62 pub vendor_id: u32,
63 pub service_number: u32,
64 pub result_block: Option<Vec<u8>>,
65}
66
67#[cfg(feature = "alloc")]
68impl ConfirmedPrivateTransferAck {
69 pub fn decode(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
70 let vendor_id = decode_ctx_unsigned(r, 0)?;
72 let service_number = decode_ctx_unsigned(r, 1)?;
74 let result_block = if !r.is_empty() {
76 match Tag::decode(r)? {
77 Tag::Opening { tag_num: 2 } => Some(decode_constructed_block_inner_bytes(r, 2)?),
78 _ => return Err(DecodeError::InvalidTag),
79 }
80 } else {
81 None
82 };
83 if !r.is_empty() {
84 return Err(DecodeError::InvalidTag);
85 }
86 Ok(Self {
87 vendor_id,
88 service_number,
89 result_block,
90 })
91 }
92}
93
94#[cfg(feature = "alloc")]
95fn decode_constructed_block_inner_bytes(
96 r: &mut Reader<'_>,
97 expected_tag_num: u8,
98) -> Result<Vec<u8>, DecodeError> {
99 let start = r.position();
101 let mut probe = *r;
102 let mut stack = Vec::new();
103 loop {
104 let tag_start = probe.position();
105 let tag = Tag::decode(&mut probe)?;
106 match tag {
107 Tag::Application {
108 tag: crate::encoding::tag::AppTag::Boolean,
109 ..
110 } => {}
111 Tag::Application { len, .. } | Tag::Context { len, .. } => {
112 probe.read_exact(len as usize)?;
113 }
114 Tag::Opening { tag_num } => stack.push(tag_num),
115 Tag::Closing { tag_num } => {
116 if let Some(opening) = stack.pop() {
117 if opening != tag_num {
118 return Err(DecodeError::InvalidTag);
119 }
120 } else if tag_num == expected_tag_num {
121 let inner_len = tag_start
122 .checked_sub(start)
123 .ok_or(DecodeError::InvalidLength)?;
124 let inner = r.read_exact(inner_len)?.to_vec();
125 match Tag::decode(r)? {
126 Tag::Closing { tag_num: closing } if closing == expected_tag_num => {}
127 _ => return Err(DecodeError::InvalidTag),
128 }
129 return Ok(inner);
130 } else {
131 return Err(DecodeError::InvalidTag);
132 }
133 }
134 }
135 }
136}
137
138#[cfg(feature = "alloc")]
139fn decode_ctx_unsigned(r: &mut Reader<'_>, expected_tag_num: u8) -> Result<u32, DecodeError> {
140 match Tag::decode(r)? {
141 Tag::Context { tag_num, len } if tag_num == expected_tag_num => {
142 decode_unsigned(r, len as usize)
143 }
144 _ => Err(DecodeError::InvalidTag),
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use crate::apdu::ConfirmedRequestHeader;
152 #[cfg(feature = "alloc")]
153 use crate::encoding::{primitives::encode_ctx_unsigned, tag::AppTag};
154 use crate::encoding::{reader::Reader, writer::Writer};
155
156 #[test]
157 fn encode_private_transfer_request() {
158 let req = ConfirmedPrivateTransferRequest {
159 vendor_id: 42,
160 service_number: 1,
161 service_parameters: Some(&[0x01, 0x02, 0x03]),
162 invoke_id: 5,
163 };
164
165 let mut buf = [0u8; 64];
166 let mut w = Writer::new(&mut buf);
167 req.encode(&mut w).unwrap();
168
169 let mut r = Reader::new(w.as_written());
170 let header = ConfirmedRequestHeader::decode(&mut r).unwrap();
171 assert_eq!(header.invoke_id, 5);
172 assert_eq!(header.service_choice, SERVICE_CONFIRMED_PRIVATE_TRANSFER);
173 assert!(!r.is_empty());
174 }
175
176 #[test]
177 fn encode_private_transfer_no_params() {
178 let req = ConfirmedPrivateTransferRequest {
179 vendor_id: 100,
180 service_number: 7,
181 service_parameters: None,
182 invoke_id: 1,
183 };
184
185 let mut buf = [0u8; 64];
186 let mut w = Writer::new(&mut buf);
187 req.encode(&mut w).unwrap();
188
189 let mut r = Reader::new(w.as_written());
190 let header = ConfirmedRequestHeader::decode(&mut r).unwrap();
191 assert_eq!(header.service_choice, SERVICE_CONFIRMED_PRIVATE_TRANSFER);
192 }
193
194 #[cfg(feature = "alloc")]
195 #[test]
196 fn decode_private_transfer_ack_preserves_nested_result_block_bytes() {
197 let mut payload = [0u8; 128];
198 let mut w = Writer::new(&mut payload);
199 encode_ctx_unsigned(&mut w, 0, 77).unwrap();
200 encode_ctx_unsigned(&mut w, 1, 9).unwrap();
201 Tag::Opening { tag_num: 2 }.encode(&mut w).unwrap();
202
203 let mut expected_inner = [0u8; 64];
204 let mut ew = Writer::new(&mut expected_inner);
205 Tag::Application {
206 tag: AppTag::UnsignedInt,
207 len: 1,
208 }
209 .encode(&mut ew)
210 .unwrap();
211 ew.write_u8(0x2A).unwrap();
212 Tag::Opening { tag_num: 0 }.encode(&mut ew).unwrap();
213 Tag::Application {
214 tag: AppTag::Boolean,
215 len: 1,
216 }
217 .encode(&mut ew)
218 .unwrap();
219 Tag::Closing { tag_num: 0 }.encode(&mut ew).unwrap();
220 w.write_all(ew.as_written()).unwrap();
221
222 Tag::Closing { tag_num: 2 }.encode(&mut w).unwrap();
223
224 let mut r = Reader::new(w.as_written());
225 let ack = ConfirmedPrivateTransferAck::decode(&mut r).unwrap();
226 assert_eq!(ack.vendor_id, 77);
227 assert_eq!(ack.service_number, 9);
228 assert_eq!(ack.result_block, Some(ew.as_written().to_vec()));
229 }
230
231 #[cfg(feature = "alloc")]
232 #[test]
233 fn decode_private_transfer_ack_rejects_trailing_or_invalid_optional_block() {
234 let mut invalid_optional = [0u8; 64];
235 let mut w = Writer::new(&mut invalid_optional);
236 encode_ctx_unsigned(&mut w, 0, 1).unwrap();
237 encode_ctx_unsigned(&mut w, 1, 2).unwrap();
238 encode_ctx_unsigned(&mut w, 3, 9).unwrap();
239 let mut r = Reader::new(w.as_written());
240 assert_eq!(
241 ConfirmedPrivateTransferAck::decode(&mut r).unwrap_err(),
242 DecodeError::InvalidTag
243 );
244
245 let mut trailing = [0u8; 64];
246 let mut w = Writer::new(&mut trailing);
247 encode_ctx_unsigned(&mut w, 0, 1).unwrap();
248 encode_ctx_unsigned(&mut w, 1, 2).unwrap();
249 Tag::Opening { tag_num: 2 }.encode(&mut w).unwrap();
250 Tag::Closing { tag_num: 2 }.encode(&mut w).unwrap();
251 encode_ctx_unsigned(&mut w, 3, 9).unwrap();
252 let mut r = Reader::new(w.as_written());
253 assert_eq!(
254 ConfirmedPrivateTransferAck::decode(&mut r).unwrap_err(),
255 DecodeError::InvalidTag
256 );
257 }
258
259 #[cfg(feature = "alloc")]
260 #[test]
261 fn decode_private_transfer_ack_distinguishes_absent_vs_empty_result_block() {
262 let mut payload = [0u8; 64];
263 let mut w = Writer::new(&mut payload);
264 encode_ctx_unsigned(&mut w, 0, 11).unwrap();
265 encode_ctx_unsigned(&mut w, 1, 22).unwrap();
266 Tag::Opening { tag_num: 2 }.encode(&mut w).unwrap();
267 Tag::Closing { tag_num: 2 }.encode(&mut w).unwrap();
268 let mut r = Reader::new(w.as_written());
269 let ack = ConfirmedPrivateTransferAck::decode(&mut r).unwrap();
270 assert_eq!(ack.result_block, Some(Vec::new()));
271 }
272}