• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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