use std::str::FromStr;
use asn1_rs::Oid;
use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder};
use serde::{Deserialize, Deserializer, Serialize};
use super::data::{get_oid_from_int, ATTRIBUTES_LOOKUP};
use crate::oid::{C509oid, C509oidRegistered};
#[derive(Debug, Clone, PartialEq)]
pub struct Attribute {
registered_oid: C509oidRegistered,
multi_value: bool,
value: Vec<AttributeValue>,
}
impl Attribute {
#[must_use]
pub fn new(oid: Oid<'static>) -> Self {
Self {
registered_oid: C509oidRegistered::new(oid, ATTRIBUTES_LOOKUP.get_int_to_oid_table()),
multi_value: false,
value: Vec::new(),
}
}
pub fn add_value(&mut self, value: AttributeValue) {
self.value.push(value);
}
pub(crate) fn get_registered_oid(&self) -> &C509oidRegistered {
&self.registered_oid
}
pub(crate) fn get_value(&self) -> &Vec<AttributeValue> {
&self.value
}
pub(crate) fn set_pen_supported(self) -> Self {
Self {
registered_oid: self.registered_oid.pen_encoded(),
multi_value: self.multi_value,
value: self.value,
}
}
pub(crate) fn set_multi_value(mut self) -> Self {
self.multi_value = true;
self
}
}
#[derive(Debug, Deserialize, Serialize)]
struct Helper {
oid: String,
value: Vec<AttributeValue>,
}
impl<'de> Deserialize<'de> for Attribute {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de> {
let helper = Helper::deserialize(deserializer)?;
let oid =
Oid::from_str(&helper.oid).map_err(|e| serde::de::Error::custom(format!("{e:?}")))?;
let mut attr = Attribute::new(oid);
for value in helper.value {
attr.add_value(value);
}
Ok(attr)
}
}
impl Serialize for Attribute {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer {
let helper = Helper {
oid: self.registered_oid.get_c509_oid().get_oid().to_string(),
value: self.value.clone(),
};
helper.serialize(serializer)
}
}
impl Encode<()> for Attribute {
fn encode<W: Write>(
&self, e: &mut Encoder<W>, ctx: &mut (),
) -> Result<(), minicbor::encode::Error<W::Error>> {
if let Some(&oid) = self
.registered_oid
.get_table()
.get_map()
.get_by_right(&self.registered_oid.get_c509_oid().get_oid())
{
e.i16(oid)?;
} else {
self.registered_oid.get_c509_oid().encode(e, ctx)?;
}
if self.value.is_empty() {
return Err(minicbor::encode::Error::message("Attribute value is empty"));
}
if self.multi_value {
e.array(self.value.len() as u64)?;
}
for value in &self.value {
value.encode(e, ctx)?;
}
Ok(())
}
}
impl Decode<'_, ()> for Attribute {
fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result<Self, minicbor::decode::Error> {
let mut attr = if d.datatype()? == minicbor::data::Type::U8 {
let i = d.i16()?;
let oid = get_oid_from_int(i).map_err(minicbor::decode::Error::message)?;
Attribute::new(oid.clone())
} else {
let c509_oid: C509oid = d.decode()?;
Attribute::new(c509_oid.get_oid())
};
if d.datatype()? == minicbor::data::Type::Array {
let len = d.array()?.ok_or_else(|| {
minicbor::decode::Error::message("Failed to get array length for attribute value")
})?;
if len == 0 {
return Err(minicbor::decode::Error::message("Attribute value is empty"));
}
for _ in 0..len {
attr.add_value(AttributeValue::decode(d, ctx)?);
}
attr = attr.set_multi_value();
} else {
let value = AttributeValue::decode(d, ctx)?;
attr.add_value(value);
}
Ok(attr)
}
}
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum AttributeValue {
Text(String),
Bytes(Vec<u8>),
}
impl Encode<()> for AttributeValue {
fn encode<W: Write>(
&self, e: &mut Encoder<W>, _ctx: &mut (),
) -> Result<(), minicbor::encode::Error<W::Error>> {
match self {
AttributeValue::Text(text) => e.str(text)?,
AttributeValue::Bytes(bytes) => e.bytes(bytes)?,
};
Ok(())
}
}
impl Decode<'_, ()> for AttributeValue {
fn decode(d: &mut Decoder<'_>, _ctx: &mut ()) -> Result<Self, minicbor::decode::Error> {
match d.datatype()? {
minicbor::data::Type::String => Ok(AttributeValue::Text(d.str()?.to_string())),
minicbor::data::Type::Bytes => Ok(AttributeValue::Bytes(d.bytes()?.to_vec())),
_ => {
Err(minicbor::decode::Error::message(
"Invalid AttributeValue, value should be either String or Bytes",
))
},
}
}
}
#[cfg(test)]
mod test_attribute {
use asn1_rs::oid;
use super::*;
#[test]
fn encode_decode_attribute_int() {
let mut buffer = Vec::new();
let mut encoder = Encoder::new(&mut buffer);
let mut attribute = Attribute::new(oid!(1.2.840 .113549 .1 .9 .1));
attribute.add_value(AttributeValue::Text("example@example.com".to_string()));
attribute
.encode(&mut encoder, &mut ())
.expect("Failed to encode Attribute");
assert_eq!(
hex::encode(buffer.clone()),
"00736578616d706c65406578616d706c652e636f6d"
);
let mut decoder = Decoder::new(&buffer);
let attribute_decoded =
Attribute::decode(&mut decoder, &mut ()).expect("Failed to decode Attribute");
assert_eq!(attribute_decoded, attribute);
}
#[test]
fn empty_attribute_value() {
let mut buffer = Vec::new();
let mut encoder = Encoder::new(&mut buffer);
let attribute = Attribute::new(oid!(1.2.840 .113549 .1 .9 .1));
attribute
.encode(&mut encoder, &mut ())
.expect_err("Failed to encode Attribute");
}
}