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