10个版本 (4个稳定版)
3.0.0 | 2024年4月3日 |
---|---|
2.0.1 | 2024年2月1日 |
1.0.0 | 2023年3月2日 |
0.9.0 | 2022年12月1日 |
0.2.0 | 2021年3月11日 |
#246 在 编码
每月下载量 448次
用于 bsn1_serde
240KB
4.5K SLoC
bsn1
bsn1
是一个Rust库,用于在 'ASN.1' 格式中进行序列化和反序列化。
什么是ASN.1?
ASN.1代表 '抽象语法表示法一',X.690是 'ITU-T' 标准中规定以下ASN.1编码格式的标准。
- 基本编码规则(BER)
- 规范编码规则(CER)
- 区分编码规则(DER)
该软件包目前支持BER和DER。
ASN.1在某些方面与 'JSON' 类似,因为它们都是标准的结构化数据序列化格式,然而,它们在以下方面有所不同。
- JSON易于人类阅读,而ASN.1对计算机来说是可读的。也就是说,ASN.1比JSON消耗更少的计算机资源(例如CPU时间)。
- ASN.1格式中有4个类别,'通用'、'应用'、'上下文特定'和'私有'。'通用'类别定义了类似于 '整数'、'布尔'、'字符串'、'序列'等的类型,就像JSON一样。更重要的是,ASN.1允许用户使用另一个类别定义新的数据类型。
ASN.1已经在全球范围内使用了很长时间,它非常稳定。例如,'传输层安全性(TLS、SSL)'、'轻量级目录访问协议(LDAP)'、'第四代移动通信系统(4G)'等等。
参见 X.690 (07/2002) 和 维基百科。
bsn1_serde
bsn1_serde
提供了与广泛使用的crate serde
中类似的 derive 宏 Serialize
和 Deserialize
。它设计用于与 bsn1
一起使用,并为 ASN.1 格式提供专门的序列化支持。
serde
因其序列化和反序列化能力而闻名,但其本质上是作为通用框架设计的。这意味着它可能无法处理所有格式的特殊功能,如 ASN.1 中的那些。ASN.1有许多独特的功能,在其他的序列化格式中不太常见,这使得它难以适应 serde
的操作方式。
bsn1_serde
通过提供针对 ASN.1 格式的宏来解决这一空白,其设计灵感来源于 serde
。
另请参阅 bsn1_serde
。
示例
'轻量级目录访问协议(LDAP)' 采用了 BER。让我们来创建/解析 LDAP 绑定操作(即登录查询)。
有关详细信息,请参阅 RFC4511。
use bsn1::{Ber, BerRef, ContentsRef, Id, IdRef, ClassTag, PCTag};
/// Creates a BindRequest from `name` and `password`.
///
/// BindRequest ::= [APPLICATION 0] SEQUENCE {
/// version INTEGER (1 .. 127),
/// name LDAPDN,
/// authentication AuthenticationChoice }
fn new_bind_request(name: &str, password: &[u8]) -> Ber {
// BindRequest is constituted of version, name, and authentication.
// '[APPLICATION 0] SEQUENCE' means "a sequence, but the class is APPLICATION, and the
// number is 0."
// The RFC does not refer to the Primitive/Constructed flag,
// but SEQUENCE is usually Constructed.
const ID_NUMBER: u32 = 0;
let id = Id::new(ClassTag::Application, PCTag::Constructed, ID_NUMBER.into());
let contents = [new_bind_version(), new_bind_name(name),
new_bind_authentication(password)];
Ber::from_id_iterator(&id, contents.iter())
}
/// Creates a `version` for bind request.
/// This function always returns 3 (the current latest version.)
fn new_bind_version() -> Ber {
Ber::from(3_i32)
}
/// Creates a `name` for bind request from `name`.
///
/// LDAPDN ::= LDAPString
/// -- Constrained to <distinguishedName> [RFC4514]
///
/// LDAPString ::= OCTET STRING -- UTF-8 encoded,
/// -- [ISO10646] characters
fn new_bind_name(name: &str) -> Ber {
Ber::from(name.as_bytes())
}
/// Creates a `simple authentication` for bind request from `password`.
///
/// AuthenticationChoice ::= CHOICE {
/// simple [0] OCTET STRING,
/// -- 1 and 2 reserved
/// sasl [3] SaslCredentials,
/// ... }
fn new_bind_authentication(password: &[u8]) -> Ber {
// 'AuthenticationChoice' is either 'simple [0] OCTET STRING' or 'sasl [3] SaslCredentials'.
// This function selects 'simple'.
//
// '[0] OCTET STRING' means "OCTET STRING, but the number is 0."
// RFC does not refer to the class and Primitive/Constructed flag.
// This function returns ContextSpecific and Primitive BER.
const ID_NUMBER: u32 = 0;
let id = Id::new(ClassTag::ContextSpecific, PCTag::Primitive, ID_NUMBER.into());
let contents: &ContentsRef = password.into();
Ber::new(&id, contents)
}
/// Tries to parse bind request and returns `(name, password)`.
fn parse_bind_request(req: &[u8]) -> Result<(&str, &[u8]), String> {
// `req` should be a 'BER' and must not include any extra octets.
let mut bytes = req;
let req = BerRef::parse(&mut bytes)
.map_err(|e| format!("Failed to parse the request as a BER: {}", e))?;
if bytes.is_empty() == false {
return Err("There are some bad octets at the end of the request.".to_string());
}
// Check the identifier of the request.
let id = req.id();
if id.class() != ClassTag::Application || id.number() != Ok(0_u8.into()) {
return Err("The id of the request is bad.".to_string());
}
// Parse the contents of the request.
// The contents should be composed of version, name, and authentication in this order.
let mut bytes: &[u8] = req.contents().as_ref();
let version = parse_bind_version(&mut bytes)?;
if version != 3 {
return Err("This function supports only version 3.".to_string());
}
let name = parse_bind_name(&mut bytes)?;
let password = parse_bind_authentication(&mut bytes)?;
Ok((name, password))
}
/// Tries to parse the version of bind request.
fn parse_bind_version(bytes: &mut &[u8]) -> Result<i32, String> {
let ber = BerRef::parse(bytes).map_err(|e| format!("Failed to parse the version: {}", e))?;
if ber.id() != IdRef::integer() {
Err("The id of the version is bad.".to_string())
} else {
ber.contents()
.to_integer()
.map_err(|e| format!("Failed to parse the version: {}", e))
}
}
/// Tries to parse the name of bind request.
fn parse_bind_name<'a>(bytes: &mut &'a [u8]) -> Result<&'a str, String> {
let ber = BerRef::parse(bytes).map_err(|e| format!("Failed to parse the name: {}", e))?;
if ber.id() != IdRef::octet_string() && ber.id() != IdRef::octet_string_constructed() {
Err("The id of the name is bad.".to_string())
} else {
let contents = ber.contents().as_ref();
std::str::from_utf8(contents).map_err(|e| format!("Failed to parse the name: {}", e))
}
}
/// Tries to parse the password of bind request.
fn parse_bind_authentication<'a>(bytes: &mut &'a [u8]) -> Result<&'a [u8], String> {
let ber = BerRef::parse(bytes)
.map_err(|e| format!("Failed to parse the authentication: {}", e))?;
if ber.id().number() == Ok(3_u8.into()) {
Err("This function supports only simple authentication".to_string())
} else if ber.id().number() == Ok(0_u8.into()) {
Ok(ber.contents().as_ref())
} else {
Err("The id of the authentication is bad".to_string())
}
}
// Create a bind request
let name = "uid=user,dc=my_company,dc=co,dc=jp";
let password = "open_sesami".as_bytes();
let request = new_bind_request(name, password);
// The client will send the byte to the server actually.
let bytes = request.as_ref();
// The LDAP server will parse the request.
let (name_, password_) = parse_bind_request(bytes).unwrap();
assert_eq!(name, name_);
assert_eq!(password, password_);
依赖关系
~595KB
~12K SLoC