use super::common::*;
use super::errors::Error;
use bitmask::*;
use ciborium::de::from_reader;
use ciborium::Value;
const REALM_CHALLENGE_LABEL: i128 = 10;
const REALM_PERSO_LABEL: i128 = 44235;
const REALM_RIM_LABEL: i128 = 44238;
const REALM_REM_LABEL: i128 = 44239;
const REALM_HASH_ALG_LABEL: i128 = 44236;
const REALM_RAK_LABEL: i128 = 44237;
const REALM_RAK_HASH_ALG_LABEL: i128 = 44240;
bitmask! {
#[derive(Debug)]
mask ClaimsSet: u8 where flags Claims {
Challenge = 0x01,
Perso = 0x02,
Rim = 0x04,
Rem = 0x08,
HashAlg = 0x10,
Rak = 0x20,
RakHashAlg = 0x40,
}
}
#[derive(Debug)]
pub struct Realm {
pub challenge: [u8; 64], pub perso: [u8; 64], pub rim: Vec<u8>, pub rem: [Vec<u8>; 4], pub hash_alg: String, pub rak: [u8; 97], pub rak_hash_alg: String,
claims_set: ClaimsSet,
}
impl Default for Realm {
fn default() -> Self {
Self::new()
}
}
impl Realm {
pub fn new() -> Self {
Self {
challenge: [0; 64],
perso: [0; 64],
rim: vec![0, 64],
rem: Default::default(),
hash_alg: String::from(""),
rak: [0; 97],
rak_hash_alg: String::from(""),
claims_set: ClaimsSet::none(),
}
}
pub fn decode(buf: &Vec<u8>) -> Result<Realm, Error> {
let v: Value = from_reader(buf.as_slice()).map_err(|e| Error::Syntax(e.to_string()))?;
let mut rc: Realm = Default::default();
if let Value::Map(contents) = v {
rc.parse(contents)?;
} else {
return Err(Error::TypeMismatch("expecting map type".to_string()));
}
rc.validate()?;
Ok(rc)
}
fn parse(&mut self, contents: Vec<(Value, Value)>) -> Result<(), Error> {
for (k, v) in contents.iter() {
if let Value::Integer(i) = k {
match (*i).into() {
REALM_CHALLENGE_LABEL => self.set_challenge(v)?,
REALM_PERSO_LABEL => self.set_perso(v)?,
REALM_RIM_LABEL => self.set_rim(v)?,
REALM_REM_LABEL => self.set_rem(v)?,
REALM_HASH_ALG_LABEL => self.set_hash_alg(v)?,
REALM_RAK_LABEL => self.set_rak(v)?,
REALM_RAK_HASH_ALG_LABEL => self.set_rak_hash_alg(v)?,
_ => continue,
}
} else {
continue;
}
}
Ok(())
}
fn validate(&self) -> Result<(), Error> {
let mandatory_claims = [
(Claims::Challenge, "challenge"),
(Claims::Perso, "personalization-value"),
(Claims::Rim, "initial-measurement"),
(Claims::Rem, "extensible-measurements"),
(Claims::HashAlg, "hash-algo-id"),
(Claims::Rak, "public-key"),
(Claims::RakHashAlg, "public-key-hash-algo-id"),
];
for (c, n) in mandatory_claims.iter() {
if !self.claims_set.contains(*c) {
return Err(Error::MissingClaim(n.to_string()));
}
}
Ok(())
}
fn set_challenge(&mut self, v: &Value) -> Result<(), Error> {
if self.claims_set.contains(Claims::Challenge) {
return Err(Error::DuplicatedClaim("challenge".to_string()));
}
let _x = v.as_bytes();
if _x.is_none() {
return Err(Error::TypeMismatch("challenge MUST be bstr".to_string()));
}
let x = _x.unwrap().clone();
let x_len = x.len();
if x_len != 64 {
return Err(Error::Sema(format!(
"challenge: expecting 64 bytes, got {x_len}"
)));
}
self.challenge[..].clone_from_slice(&x);
self.claims_set.set(Claims::Challenge);
Ok(())
}
fn set_rak_hash_alg(&mut self, v: &Value) -> Result<(), Error> {
if self.claims_set.contains(Claims::RakHashAlg) {
return Err(Error::DuplicatedClaim(
"public-key-hash-algo-id".to_string(),
));
}
self.rak_hash_alg = to_hash_alg(v, "public-key-hash-algo-id")?;
self.claims_set.set(Claims::RakHashAlg);
Ok(())
}
fn set_hash_alg(&mut self, v: &Value) -> Result<(), Error> {
if self.claims_set.contains(Claims::HashAlg) {
return Err(Error::DuplicatedClaim("hash-algo-id".to_string()));
}
self.hash_alg = to_hash_alg(v, "hash-algo-id")?;
self.claims_set.set(Claims::HashAlg);
Ok(())
}
fn set_rim(&mut self, v: &Value) -> Result<(), Error> {
if self.claims_set.contains(Claims::Rim) {
return Err(Error::DuplicatedClaim("initial-measurement".to_string()));
}
self.rim = to_measurement(v, "initial-measurement")?;
self.claims_set.set(Claims::Rim);
Ok(())
}
fn set_rak(&mut self, v: &Value) -> Result<(), Error> {
if self.claims_set.contains(Claims::Rak) {
return Err(Error::DuplicatedClaim("public-key".to_string()));
}
let _x = v.as_bytes();
if _x.is_none() {
return Err(Error::TypeMismatch("public-key MUST be bstr".to_string()));
}
let x = v.as_bytes().unwrap().clone();
let x_len = x.len();
if x_len != 97 {
return Err(Error::Sema(format!(
"public-key: expecting 97 bytes, got {}",
x_len
)));
}
self.rak[..].clone_from_slice(&x);
self.claims_set.set(Claims::Rak);
Ok(())
}
fn set_rem(&mut self, v: &Value) -> Result<(), Error> {
if self.claims_set.contains(Claims::Rem) {
return Err(Error::DuplicatedClaim(
"extensible-measurements".to_string(),
));
}
let _x = v.as_array();
if _x.is_none() {
return Err(Error::TypeMismatch(
"extensible-measurements MUST be array".to_string(),
));
}
let x = _x.unwrap().clone();
let x_len = x.len();
if x_len != 4 {
return Err(Error::Sema(format!(
"extensible-measurements: expecting 4 slots, got {}",
x_len
)));
}
for (i, xi) in x.iter().enumerate() {
self.rem[i] = to_measurement(xi, format!("extensible-measurement[{}]", i).as_str())?;
}
self.claims_set.set(Claims::Rem);
Ok(())
}
fn set_perso(&mut self, v: &Value) -> Result<(), Error> {
if self.claims_set.contains(Claims::Perso) {
return Err(Error::DuplicatedClaim("personalization-value".to_string()));
}
let _x = v.as_bytes();
if _x.is_none() {
return Err(Error::TypeMismatch(
"personalization-value MUST be bstr".to_string(),
));
}
let x = _x.unwrap().clone();
let x_len = x.len();
if x_len != 64 {
return Err(Error::Sema(format!(
"personalization value: expecting 64 bytes, got {}",
x_len
)));
}
self.perso[..].clone_from_slice(&x);
self.claims_set.set(Claims::Perso);
Ok(())
}
pub fn get_realm_key(&self) -> Result<[u8; 97], Error> {
let rak = self.rak;
if rak.is_empty() {
return Err(Error::MissingClaim("No realm Key".to_string()));
}
Ok(rak)
}
pub fn get_rak_hash_alg(&self) -> Result<String, Error> {
let rak_hash_alg = self.rak_hash_alg.clone();
if rak_hash_alg.is_empty() {
return Err(Error::MissingClaim("No realm hash alg".to_string()));
}
Ok(rak_hash_alg)
}
}
mod tests {
use super::*;
use hex_literal::hex;
const TEST_REALM_CLAIMS_OK: &[u8; 438] = include_bytes!("../../testdata/realm-claims.cbor");
const TEST_REALM_CLAIMS_BAD_EXTRA_NUMERIC_KEY: &[u8; 457] =
include_bytes!("../../testdata/realm-claims+spurious-numeric-key.cbor");
const TEST_REALM_CLAIMS_BAD_EXTRA_TEXT_KEY: &[u8; 464] =
include_bytes!("../../testdata/realm-claims+spurious-text-key.cbor");
const TEST_REALM_CLAIMS_BAD_MISSING_NONCE: &[u8; 371] =
include_bytes!("../../testdata/realm-claims-missing-challenge.cbor");
#[test]
fn realm_ok() {
let _r = Realm::decode(&TEST_REALM_CLAIMS_OK.to_vec()).expect("successful decode");
}
#[test]
fn realm_good_with_extra_claim_using_numeric_key() {
let _r = Realm::decode(&TEST_REALM_CLAIMS_BAD_EXTRA_NUMERIC_KEY.to_vec())
.expect("successful decode");
}
#[test]
fn realm_good_with_extra_claim_using_text_key() {
let _r = Realm::decode(&TEST_REALM_CLAIMS_BAD_EXTRA_TEXT_KEY.to_vec())
.expect("successful decode");
}
#[test]
fn realm_bad_missing_mandatory_claim() {
assert!(Realm::decode(&TEST_REALM_CLAIMS_BAD_MISSING_NONCE.to_vec()).is_err());
}
#[test]
fn realm_bad_empty_map() {
let buf = hex!("a0").to_vec();
assert!(Realm::decode(&buf).is_err());
}
#[test]
fn realm_bad_unknown_eat() {
let buf = hex!("a10a48deadbeefdeadbeef").to_vec();
assert!(Realm::decode(&buf).is_err());
}
#[test]
fn realm_bad_rubbish_cbor() {
let buf = hex!("ffffffff").to_vec();
assert!(Realm::decode(&buf).is_err());
}
#[test]
fn realm_bad_challenge_type() {
let buf = hex!("a10a0a").to_vec();
assert!(Realm::decode(&buf).is_err());
}
}