2个不稳定版本
0.2.0 | 2024年4月3日 |
---|---|
0.1.0 | 2024年2月1日 |
#1098 in 编码
64每月下载量
345KB
7K SLoC
bsn1_serde
bsn1_serde
提供了与广泛使用的crate serde
相似的derive宏Serialize
和Deserialize
,旨在与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