| // Copyright 2026 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use aes::cipher::{BlockCipherEncrypt, KeyInit}; |
| |
| /// Set Identity Hash (SIH) function as defined in CSIS 1.1 Specification, |
| /// Section 4.7. `sih(k, r) = e(k, r') mod 2^24` |
| /// where `r' = padding || r`, padding is 104 bits of 0. |
| /// The LSO of r becomes the LSO of r'. |
| pub fn set_identity_hash(key: &[u8; 16], r: &[u8; 3]) -> [u8; 3] { |
| let mut plaintext = [0u8; 16]; |
| |
| // r' = padding || r. LSO of r is LSO of r'. |
| // Since Bluetooth is little endian, LSO is at index 0. |
| plaintext[0] = r[0]; |
| plaintext[1] = r[1]; |
| plaintext[2] = r[2]; |
| // The rest of the bytes are already 0 (padding). |
| |
| // Bluetooth `e` function uses AES-128 but is LSB-first (little endian). |
| // The `aes` crate is MSB-first. We need to reverse the input and output. |
| let mut key_reversed = *key; |
| key_reversed.reverse(); |
| plaintext.reverse(); |
| |
| let key_block = key_reversed.into(); |
| let mut block = plaintext.into(); |
| |
| let cipher = aes::Aes128::new(&key_block); |
| cipher.encrypt_block(&mut block); |
| |
| // sih keeps the least significant 24 bits of e, the last three octets of |
| // the MSB-first block, returned LSO first. |
| [block[15], block[14], block[13]] |
| } |
| |
| /// Sample values from the CSIS v1.1 specification, least significant octet |
| /// first. |
| #[cfg(test)] |
| pub(crate) mod test_vectors { |
| /// Appendix A.1, `k`. |
| pub(crate) const SIRK: [u8; 16] = [ |
| 0xcd, 0xcc, 0x72, 0xdd, 0x86, 0x8c, 0xcd, 0xce, 0x22, 0xfd, 0xa1, 0x21, 0x09, 0x7d, 0x7d, |
| 0x45, |
| ]; |
| /// Appendix A.1, `r`. In MSB-first notation, 0x69f563. |
| pub(crate) const PRAND: [u8; 3] = [0x63, 0xf5, 0x69]; |
| /// Appendix A.1, `sih(k, r)`. In MSB-first notation, 0x1948da. |
| pub(crate) const HASH: [u8; 3] = [0xda, 0x48, 0x19]; |
| |
| /// The RSI that a Set Member holding `SIRK` advertises for `PRAND`, which |
| /// is `hash || prand`. |
| pub(crate) const RSI: [u8; 6] = [HASH[0], HASH[1], HASH[2], PRAND[0], PRAND[1], PRAND[2]]; |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::test_vectors::{HASH, PRAND, SIRK}; |
| use super::*; |
| |
| #[test] |
| fn set_identity_hash_matches_the_spec_vector() { |
| assert_eq!(set_identity_hash(&SIRK, &PRAND), HASH); |
| } |
| } |