1 use aes::cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit};
2 use aes::Aes128;
3 use clap::Parser;
4 use rand::Rng;
5 
6 #[derive(Parser, Debug)]
7 #[clap(author, version, about, long_about = None)]
8 struct Args {
9     /// The command task to perform [verify|generate]
10     /// e.g.
11     ///     irk-calculator -c verify -i "..." -a "..."
12     #[clap(short, long)]
13     command: String,
14     /// The Identity Resolving Key
15     #[clap(short, long)]
16     irk: String,
17     /// The addres to verify if verifying
18     #[clap(short, long, required(false), default_value = "00:00:00:00:00:00")]
19     address: String,
20 }
21 
e(key: [u8; 16], plaintext_data: [u8; 16]) -> [u8; 16]22 fn e(key: [u8; 16], plaintext_data: [u8; 16]) -> [u8; 16] {
23     println!("=====[ e ]=====");
24     let mut key_reversed = key.clone();
25     key_reversed.reverse();
26     println!("Reversed Key: {:02X?}", key_reversed);
27     let cipher = Aes128::new(&GenericArray::from(key_reversed));
28     let mut block = GenericArray::from(plaintext_data);
29     println!("Data: {:02X?}", block);
30     cipher.encrypt_block(&mut block);
31     println!("Encrypted data: {:02X?}", block);
32     let mut ret = [0u8; 16];
33     ret.clone_from_slice(block.iter().as_slice());
34     println!("=====[ /e ]=====");
35     ret
36 }
37 
38 /// ah function as defined in BT Spec Core v5.2 pg 1626
39 ///
40 /// k is 128 bits
41 /// r is 24 bits
42 /// padding is 104 bits
43 ///
44 /// returns 3 byte array
_ah(k: [u8; 16], r: [u8; 3], padding: [u8; 13]) -> [u8; 3]45 fn _ah(k: [u8; 16], r: [u8; 3], padding: [u8; 13]) -> [u8; 3] {
46     println!("=====[ ah ]=====");
47     let mut padded_r: [u8; 16] = [0u8; 16];
48     // Pad the r to become r'
49     padded_r[..13].clone_from_slice(&padding);
50     padded_r[13..].clone_from_slice(&r);
51     println!("K: {:02X?}", k);
52     println!("R: {:02X?}", r);
53     println!("R': {:02X?}", padded_r);
54 
55     // Create data
56     let encrypted_data = e(k, padded_r);
57     // Mod 2^24 (Only take last 3 bytes)
58     let mut ret = [0u8; 3];
59     let mut i = 0;
60     for b in &encrypted_data[13..] {
61         ret[i] = *b;
62         i += 1;
63     }
64     println!("Mod 2^24 data: {:02X?}", ret);
65     println!("=====[ /ah ]=====");
66     ret
67 }
68 
ah(k: [u8; 16], r: [u8; 3]) -> [u8; 3]69 fn ah(k: [u8; 16], r: [u8; 3]) -> [u8; 3] {
70     let padding = [0u8; 13];
71     _ah(k, r, padding)
72 }
73 
to_hex_string(bytes: Vec<u8>) -> String74 fn to_hex_string(bytes: Vec<u8>) -> String {
75     let s: Vec<String> = bytes.iter().map(|b| format!("{:02X}", b)).collect();
76 
77     s.join(":")
78 }
79 
parse_hex(hexstr: &str) -> Vec<u8>80 fn parse_hex(hexstr: &str) -> Vec<u8> {
81     let mut hex_bytes = hexstr
82         .as_bytes()
83         .iter()
84         .filter_map(|b| match b {
85             b'0'..=b'9' => Some(b - b'0'),
86             b'a'..=b'f' => Some(b - b'a' + 10),
87             b'A'..=b'F' => Some(b - b'A' + 10),
88             _ => None,
89         })
90         .fuse();
91 
92     let mut bytes = Vec::new();
93     while let (Some(h), Some(l)) = (hex_bytes.next(), hex_bytes.next()) {
94         bytes.push(h << 4 | l)
95     }
96     bytes
97 }
98 
parse_irk(irk: String) -> Vec<u8>99 fn parse_irk(irk: String) -> Vec<u8> {
100     let irk_byte_array = parse_hex(irk.as_str());
101     assert_eq!(16, irk_byte_array.len(), "IRK '{:02X?}' must be 16 octets!", irk_byte_array);
102     irk_byte_array
103 }
104 
parse_address(address: String) -> Vec<u8>105 fn parse_address(address: String) -> Vec<u8> {
106     let address_byte_array = parse_hex(address.as_str());
107     assert_eq!(
108         6,
109         address_byte_array.len(),
110         "Address '{:02X?}' must be 6 octets!",
111         address_byte_array
112     );
113     address_byte_array
114 }
115 
116 // TODO(optedoblivion): Verify address is RPA
verify_irk_address(irk: String, address: String) -> bool117 fn verify_irk_address(irk: String, address: String) -> bool {
118     println!("Verifying '{}' matches '{}'", irk, address);
119 
120     // IRK
121     let irk_byte_array = parse_irk(irk);
122     println!("IRK Byte Array: {:02X?}", irk_byte_array);
123 
124     // Address
125     let address_byte_array = parse_address(address);
126     println!("Address Byte Array: {:02X?}", address_byte_array);
127 
128     // prand
129     let mut prand = [0u8; 3];
130     prand.clone_from_slice(&address_byte_array[..=2]);
131     println!("prand: {:02X?}", prand);
132 
133     // Hash
134     let given_hash = &address_byte_array[3..];
135     println!("Given hash: {:02X?}", given_hash);
136 
137     let mut irk_slice = [0u8; 16];
138     irk_slice.clone_from_slice(&irk_byte_array[..]);
139     let hash = ah(irk_slice, prand);
140     println!("Given hash: {:02X?}", given_hash);
141     println!("Calculated hash: {:02X?}", hash);
142     println!("IRK + Address combination is valid: {}", given_hash == hash);
143     given_hash == hash
144 }
145 
generate_irk_address(irk: String) -> String146 fn generate_irk_address(irk: String) -> String {
147     println!("Generating new address with '{}'", irk);
148 
149     // IRK
150     let irk_byte_array = parse_irk(irk);
151     println!("IRK Byte Array: {:02X?}", irk_byte_array);
152 
153     // prand
154     let prand = rand::thread_rng().gen::<[u8; 3]>();
155     println!("prand: {:02X?}", prand);
156 
157     let mut irk_slice = [0u8; 16];
158     irk_slice.clone_from_slice(&irk_byte_array[..]);
159     let hash = ah(irk_slice, prand);
160     println!("Calculated hash: {:02X?}", hash);
161 
162     let mut calculated_address = [0u8; 6];
163     println!("len: {}", prand.len());
164     calculated_address[0] = prand[0];
165     calculated_address[1] = prand[1];
166     calculated_address[2] = prand[2];
167     calculated_address[3] = hash[0];
168     calculated_address[4] = hash[1];
169     calculated_address[5] = hash[2];
170     println!("Calculated Address: {}", to_hex_string(calculated_address.to_vec()));
171     to_hex_string(calculated_address.to_vec())
172 }
173 
main()174 fn main() {
175     let args = Args::parse();
176 
177     match args.command.as_str() {
178         "verify" => {
179             verify_irk_address(args.irk, args.address);
180         }
181         "generate" => {
182             generate_irk_address(args.irk);
183         }
184         _ => {
185             println!("Invalid command!");
186         }
187     }
188 }
189 
190 #[test]
test_verify_good_combos()191 fn test_verify_good_combos() {
192     assert_eq!(
193         true,
194         verify_irk_address(
195             String::from("0102030405060708090a0b0c0d0e0f10"),
196             String::from("5B:89:68:1E:4E:19"),
197         )
198     );
199     assert_eq!(
200         true,
201         verify_irk_address(
202             String::from("0102030405060708090a0b0c0d0e0f10"),
203             String::from("79:CB:92:70:BE:B3"),
204         )
205     );
206     assert_eq!(
207         true,
208         verify_irk_address(
209             String::from("0102030405060708090a0b0c0d0e0f10"),
210             String::from("5D:EC:DA:8C:33:AE"),
211         )
212     );
213 }
214 
215 #[test]
test_verify_bad_combos()216 fn test_verify_bad_combos() {
217     assert_eq!(
218         false,
219         verify_irk_address(
220             String::from("0102030405060708090a0b0c0d0e0f10"),
221             String::from("60:89:68:1E:4E:19"),
222         )
223     );
224 }
225 
_validate_address_byte(i: usize, address: &String)226 fn _validate_address_byte(i: usize, address: &String) {
227     println!("address: {:?}", address);
228     let vs: Vec<String> = vec![
229         address.chars().nth(i).unwrap().to_string(),
230         address.chars().nth(i + 1).unwrap().to_string(),
231     ];
232     let byte_string = vs.join("");
233     match parse_hex(&byte_string.as_str()) {
234         a => {
235             println!("herp: {:?}", a);
236             assert_eq!(1, a.len());
237         }
238     }
239 }
240 
241 #[test]
test_validate_good_address_byte()242 fn test_validate_good_address_byte() {
243     let good_address = String::from("1A:34:AF:78:98:76");
244     println!("{:?}", good_address.chars().nth(0).unwrap().to_string().as_str());
245     _validate_address_byte(0, &good_address);
246 }
247 
248 #[test]
249 #[should_panic]
test_validate_bad_address_byte()250 fn test_validate_bad_address_byte() {
251     let bad_address = String::from("ZX:12:34:56:78:90");
252     _validate_address_byte(0, &bad_address);
253 }
254 
255 #[test]
test_generate_rpa()256 fn test_generate_rpa() {
257     let address = generate_irk_address(String::from("0102030405060708090a0b0c0d0e0f10"));
258     _validate_address_byte(0, &address);
259     assert_eq!(address.chars().nth(2).unwrap(), ':');
260     _validate_address_byte(3, &address);
261     assert_eq!(address.chars().nth(5).unwrap(), ':');
262     _validate_address_byte(6, &address);
263     assert_eq!(address.chars().nth(8).unwrap(), ':');
264     _validate_address_byte(9, &address);
265     assert_eq!(address.chars().nth(11).unwrap(), ':');
266     _validate_address_byte(12, &address);
267 }
268