2个不稳定版本

0.2.0 2024年4月3日
0.1.0 2024年2月1日

#1098 in 编码

Download history

64每月下载量

GPL-3.0-only

345KB
7K SLoC

bsn1_serde

bsn1_serde提供了与广泛使用的crate serde 相似的derive宏SerializeDeserialize,旨在与bsn1一起使用,为ASN.1格式提供专门的序列化支持。

serde以其序列化/反序列化功能而闻名,但它本质上是一个通用框架。这意味着它可能无法处理所有格式的特殊功能,例如ASN.1中的那些。ASN.1有许多在其他序列化格式中不常见的独特功能,这使得它难以适应serde的方式。

bsn1_serde通过提供针对ASN.1格式定制的宏来填补这一空白,并从serde的设计中汲取灵感。

另请参阅 bsn1

示例

'轻量级目录访问协议 (LDAP)' 采用BER。让我们创建/解析LDAP绑定请求(即登录查询。)

请注意,BER由标识符和内容组成。标识符由类、原始/构造标志和数字组成。

有关详细信息,请参阅RFC4511

use bsn1::{ContentsRef, Error, Id, IdRef, Length};
use bsn1_serde::{de, from_ber, ser, to_ber, Deserialize, OctetString, Serialize};
use std::io::Write;

/// `BindRequest` represents the Bind Request (= login query) of LDAP.
/// We support only simple authentication (= password) here.
///
/// BindRequest ::= [APPLICATION 0] SEQUENCE {
///          version INTEGER (1 .. 127),
///          name LDAPDN,
///          authentication AuthenticationChoice }
///
/// LDAPDN ::= LDAPString
///            -- Constrained to <distinguishedName> [RFC4514]
///
/// LDAPString ::= OCTET STRING -- UTF-8 encoded,
///                             -- [ISO10646] characters
///
/// AuthenticationChoice ::= CHOICE {
///      simple                  [0] OCTET STRING,
///      -- 1 and 2 reserved
///      sasl                    [3] SaslCredentials,
///      ... }
///
/// Note that '[APPLICATION 0] SEQUENCE' stands for
/// "SEQUENCE, but the id class is 'APPLICATION', the id number is 0."
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[bsn1_serde(id = Sequence, tag_class = APPLICATION, tag_num = 0)]
struct BindRequest {
    version: i32,
    #[bsn1_serde(to = "OctetString::new", try_from = "OctetString")]
    name: String,
    authentication: SimpleAuth,
}

/// SimpleAuth represents the password.
///
/// Unfortunately, we cannot derive the implementation of the trait from `Serialize`
/// and `Deserialize` for this struct for now.
///
/// We implement them later.
///
/// AuthenticationChoice ::= CHOICE {
///      simple                  [0] OCTET STRING,
///      -- 1 and 2 reserved
///      sasl                    [3] SaslCredentials,
///      ... }
#[derive(Debug, PartialEq, Eq)]
struct SimpleAuth(Vec<u8>);

impl ser::Serialize for SimpleAuth {
    fn write_id<W: ?Sized + Write>(&self, buffer: &mut W) -> Result<(), Error> {
        // '[0] OCTET STRING' means "Octet String, but the id number is 0."
        let mut id: Id = IdRef::octet_string().into();
        id.set_number(0_u8.into());

        buffer.write_all(id.as_ref() as &[u8]).map_err(Error::from)
    }

    fn write_der_contents<W: ?Sized + Write>(&self, buffer: &mut W) -> Result<(), Error> {
        buffer.write_all(&self.0[..]).map_err(Error::from)
    }

    fn id_len(&self) -> Result<Option<usize>, Error> {
        // The result is always `Ok(Some(1))`.
        // You can write as `return Ok(Some(1))`, of cause.
        let mut id: Id = IdRef::octet_string().into();
        id.set_number(0_u8.into());
        Ok(Some(id.len()))
    }

    fn der_contents_len(&self) -> Result<Option<usize>, Error> {
        Ok(Some(self.0.len()))
    }
}

impl de::Deserialize for SimpleAuth {
    unsafe fn from_ber(id: &IdRef, length: Length, contents: &ContentsRef) -> Result<Self, Error> {
        // Sanitize the id, however, the spec of RFC is ambiguous.
        //
        // '[0] OCTET STRING' does not refer to the class, but it is unnatural to change the number
        // of UNIVERSAL class. (OCTET STRING is UNIVERSAL.)
        //
        // BER allows both primitive and constructed OCTET STRING.
        //
        // Here, we check only the number.
        if id.number() != Ok(0_u8.into()) {
            return Err(Error::UnmatchedId);
        }

        // `SimpleAuth` should be deserialized as OCTET STRING except for the id.
        // We pass dummy id to delegate the task to `OctetString`.
        let slice: OctetString = OctetString::from_ber(IdRef::octet_string(), length, contents)?;
        Ok(Self(slice.into_vec()))
    }

    fn from_der(id: &IdRef, contents: &ContentsRef) -> Result<Self, Error> {
        // See the comment of [`from_ber`].
        if id.number() != Ok(0_u8.into()) {
            return Err(Error::UnmatchedId);
        }

        let slice: OctetString = OctetString::from_der(IdRef::octet_string(), contents)?;
        Ok(Self(slice.into_vec()))
    }
}

fn main() {
    // Build `BindRequest`.
    let version: i32 = 3; // The current LDAP version is '3'.
    let name: String = "uid=my_name,dc=my_company,dc=co,dc=jp".to_string();
    let password: Vec<u8> = Vec::from("open_sesami".as_bytes());

    let req = BindRequest {
        version,
        name,
        authentication: SimpleAuth(password),
    };

    // Serialize and deserialize `req`.
    // The result should be same to `req`.
    let serialized = to_ber(&req).unwrap();
    let deserialized: BindRequest = from_ber(&serialized).unwrap();

    assert!(req == deserialized);
}

依赖项

~0.8–1.3MB
~29K SLoC