use uuid::Uuid;
pub const AWEAR_SERVICE_UUID: Uuid =
Uuid::from_u128(0xFC740001_0291_41FC_A9B0_45951B5B01D7);
pub const AWEAR_TX_CHARACTERISTIC: Uuid =
Uuid::from_u128(0xFC740002_0291_41FC_A9B0_45951B5B01D7);
pub const AWEAR_RX_CHARACTERISTIC: Uuid =
Uuid::from_u128(0xFC740003_0291_41FC_A9B0_45951B5B01D7);
pub const NORDIC_DFU_SERVICE_UUID: Uuid =
Uuid::from_u128(0x8EC90001_F315_4F60_9FB8_838830DAEA50);
pub const LUCA_MAGIC: &[u8; 4] = b"LUCA";
pub const LUCA_HEADER_SIZE: usize = 36;
pub const AWEAR_CONNECTED_PREFIX: &str = "AWEAR_CONNECTED:";
pub const AWEAR_READY_PREFIX: &str = "AWEAR_READY:";
pub const CHALLENGE_REPLY_PREFIX: &str = "CRPL:";
pub const CHALLENGE_CONSTANT: u32 = 0xD4C3_B2A1;
pub const CHALLENGE_SYMMETRIC_KEY: [u8; 16] = [
0x4e, 0xe1, 0xfb, 0xef, 0x87, 0x98, 0xe9, 0x4a,
0x17, 0xbb, 0x94, 0x58, 0x98, 0xc1, 0x89, 0x8f,
];
pub const EEG_FREQUENCY: f64 = 256.0;
pub const EEG_SAMPLE_BITS: usize = 16;
pub const EEG_MAX_VALUE: i32 = 8_388_607;
pub const EEG_MIN_VALUE: i32 = -8_388_608;
pub const SCAN_TIMEOUT_SECS: u64 = 10;
pub const CONNECT_TIMEOUT_SECS: u64 = 30;
pub const RECONNECT_TIMEOUT_SECS: u64 = 10;
pub const RECONNECTION_MAX_RETRIES: u32 = 10;
pub fn compute_challenge_reply(challenge_hex: &str) -> Option<String> {
let challenge_val = u32::from_str_radix(challenge_hex, 16).ok()?;
let mut message = [0u8; 8];
message[..4].copy_from_slice(&challenge_val.to_be_bytes());
message[4..8].copy_from_slice(&CHALLENGE_CONSTANT.to_le_bytes());
use hmac::{Hmac, Mac};
use sha2::Sha256;
type HmacSha256 = Hmac<Sha256>;
let mut mac = HmacSha256::new_from_slice(&CHALLENGE_SYMMETRIC_KEY).ok()?;
mac.update(&message);
let result = mac.finalize().into_bytes();
let reply_hex: String = result[..8]
.iter()
.map(|b| format!("{:02X}", b))
.collect();
Some(format!("{}{}", CHALLENGE_REPLY_PREFIX, reply_hex))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn challenge_reply_returns_crpl_prefix() {
let reply = compute_challenge_reply("AABBCCDD").unwrap();
assert!(reply.starts_with("CRPL:"));
assert_eq!(reply.len(), 5 + 16);
}
#[test]
fn challenge_reply_deterministic() {
let a = compute_challenge_reply("12345678").unwrap();
let b = compute_challenge_reply("12345678").unwrap();
assert_eq!(a, b);
}
#[test]
fn challenge_reply_different_inputs_differ() {
let a = compute_challenge_reply("00000000").unwrap();
let b = compute_challenge_reply("FFFFFFFF").unwrap();
assert_ne!(a, b);
}
#[test]
fn challenge_reply_invalid_hex_returns_none() {
assert!(compute_challenge_reply("ZZZZZZZZ").is_none());
assert!(compute_challenge_reply("").is_none());
}
#[test]
fn uuids_are_distinct() {
assert_ne!(AWEAR_SERVICE_UUID, AWEAR_TX_CHARACTERISTIC);
assert_ne!(AWEAR_TX_CHARACTERISTIC, AWEAR_RX_CHARACTERISTIC);
}
}