1 // Copyright 2023, The Android Open Source Project
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
15 //! NFC CRC calculation
16 //! Based on ISO/IEC_13239 (FCS 16 bit, CCITT)
17 //! ISO/IEC_15693-3 Annex C used for additional reference
18
19 use crate::rf;
20
21 /// Error describing why CRC verification failed
22 pub enum CrcVerificationError {
23 /// Invalid input length (<= 2 bytes) for CRC check
24 InvalidInputLength,
25 /// Type F & V are not covered here yet (TODO)
26 UnsupportedTechnology,
27 /// CRC Verification failed (CRC invalid or missing)
28 VerificationFailed,
29 }
30
crc16(data: &[u8], initial: u16, invert: bool) -> (u8, u8)31 fn crc16(data: &[u8], initial: u16, invert: bool) -> (u8, u8) {
32 let mut w_crc: u16 = initial;
33
34 for &byte in data {
35 let mut temp: u16 = (byte as u16) ^ (w_crc & 0x00FF);
36 temp = (temp ^ (temp << 4)) & 0xFF;
37 w_crc = (w_crc >> 8) ^ (temp << 8) ^ (temp << 3) ^ (temp >> 4);
38 }
39 if invert {
40 w_crc = !w_crc;
41 }
42 ((w_crc & 0xFF) as u8, ((w_crc >> 8) & 0xFF) as u8)
43 }
44
crc16a(data: &[u8]) -> (u8, u8)45 fn crc16a(data: &[u8]) -> (u8, u8) {
46 // [DIGITAL]
47 // The CRC_A MUST be calculated as defined in [ISO/IEC_13239],
48 // but the initial register content MUST be 6363h
49 // and the register content MUST not be inverted after calculation
50 crc16(data, 0x6363, false)
51 }
52
crc16b(data: &[u8]) -> (u8, u8)53 fn crc16b(data: &[u8]) -> (u8, u8) {
54 // [DIGITAL]
55 // The CRC_B MUST be calculated as defined in [ISO/IEC_13239].
56 // The initial register content MUST be all ones (FFFFh)
57 crc16(data, 0xFFFF, true)
58 }
59
60 /// Verify CRC is valid for provided NFC technology
verify_crc(technology: rf::Technology, data: &[u8]) -> Result<(), CrcVerificationError>61 pub fn verify_crc(technology: rf::Technology, data: &[u8]) -> Result<(), CrcVerificationError> {
62 if data.len() <= 2 {
63 return Err(CrcVerificationError::InvalidInputLength);
64 }
65
66 let (crc_lo, crc_hi) = match technology {
67 rf::Technology::NfcA => crc16a(&data[..data.len() - 2]),
68 rf::Technology::NfcB => crc16b(&data[..data.len() - 2]),
69 _ => return Err(CrcVerificationError::UnsupportedTechnology),
70 };
71
72 if crc_lo == data[data.len() - 2] && crc_hi == data[data.len() - 1] {
73 Ok(())
74 } else {
75 Err(CrcVerificationError::VerificationFailed)
76 }
77 }
78
79 #[cfg(test)]
80 mod test {
81 use super::*;
82
83 #[test]
test_verify_crc_type_a()84 fn test_verify_crc_type_a() {
85 // Check validity of CRC16A calculation based on HLTA (SLP_REQ)
86 assert!(verify_crc(rf::Technology::NfcA, &[0x50, 0x00, 0x57, 0xcd]).is_ok());
87 }
88
89 #[test]
test_verify_crc_type_b()90 fn test_verify_crc_type_b() {
91 // Check validity of crc16b based on REQB (SENSB_REQ)
92 assert!(verify_crc(rf::Technology::NfcB, &[0x05, 0x00, 0x00, 0x71, 0xff]).is_ok());
93 // Check validity of crc16b based on WUPB (ALLB_REQ)
94 assert!(verify_crc(rf::Technology::NfcB, &[0x05, 0x00, 0x08, 0x39, 0x73]).is_ok());
95 }
96
97 #[test]
test_verify_crc_invalid_length()98 fn test_verify_crc_invalid_length() {
99 assert!(matches!(
100 verify_crc(rf::Technology::NfcA, &[0x00]),
101 Err(CrcVerificationError::InvalidInputLength)
102 ));
103 assert!(matches!(
104 verify_crc(rf::Technology::NfcB, &[0x00, 0x01]),
105 Err(CrcVerificationError::InvalidInputLength)
106 ));
107 }
108
109 #[test]
test_verify_crc_verification_failed()110 fn test_verify_crc_verification_failed() {
111 // HLTA must end with 57CDh
112 assert!(matches!(
113 verify_crc(rf::Technology::NfcA, &[0x50, 0x00, 0x00, 0x00]),
114 Err(CrcVerificationError::VerificationFailed)
115 ));
116 // REQB must end with 71ffh
117 assert!(matches!(
118 verify_crc(rf::Technology::NfcB, &[0x05, 0x00, 0x00, 0x00, 0x00]),
119 Err(CrcVerificationError::VerificationFailed)
120 ));
121 }
122 }
123