7 个版本

0.3.2 2024年1月1日
0.3.1 2023年12月28日
0.2.9 2022年10月17日
0.1.0 2022年9月30日

#276 in 加密学

Download history 55/week @ 2024-04-03

每月55次下载

BSD-3-Clause

115KB
2K SLoC

ncryptf-rs

Rust 绑定 ncryptf

docs.rs Crates.io (recent)

rust ncryptf 绑定旨在与其它语言的绑定具有相似的 API,以方便阅读。

安装

您可以通过 cargo 将 ncryptf 添加到您的项目中的 Cargo.toml

ncryptf-rs = { git = "github.com/ncryptf/ncryptf-rs" }

Ncryptf 支持 2 个可选功能

  • rocket 用于 rocket.rs 绑定
  • client 提供一个 reqwest 客户端,您可以使用它来与 ncryptf 交互。

导出

ncryptf 为了便利和必要性重新导出了一些额外的库。

Rocket

Ncryptf 使用了修改版的 rocket.rs (0.5.0-rc: see https://github.com/charlesportwoodii/rocket.rs/tree/peek_buffer) 来获取请求数据,以使用 Rocket.rs 进行身份验证。强烈建议将这个版本的 rocket (及其他重新导出的库) 存储在一个 common 工作区中,以消除在每个工作区成员中添加相同的 cargo 依赖项的需要。

如果您在 main.rs 中使用 rocket.rs 的 proc_macro,您仍然需要导入该宏。

rocket = {  features = ["tls", "secrets", "json"] }

库映射

当使用 ncryptf 的默认重新导出时,导入的结构体名称将位于 ncryptf::<lib> 命名空间下,这对 rust-analyzer 和开发者来说可能会有些困惑。为了使 rust-analyzer 和编译器更加合理,请考虑使用一个 common 工作区,该工作区重新导出 ncryptf、rocket 和任何其他功能。

示例

common
  \ - mod.rs
    - ncryptflib.rs

在您的 mod.rslib.rs 中有以下内容

pub mod ncryptflib

然后在您的 ncryptflib.rs 中有以下内容

pub use ncryptf::{
    auth,
    client,
    ek_route,
    rocket,
    Authorization,
    Keypair,
    NcryptfError,
    Request,
    Response,
    Token,
    randombytes_buf
};

然后您可以在代码的其他地方使用 pub use ncryptflib as ncryptf,以便在不冲突的情况下,在标准命名空间下使用标准库。

HMAC+HKDF 身份验证

HMAC+HKDF身份验证是一种身份验证方法,它确保请求在传输过程中未被篡改。这不仅为网络层操作提供了弹性,还能抵御中间人攻击。

从高层次来看,HMAC签名是根据原始请求体、HTTP方法、URI(如果有查询参数)以及当前日期创建的。除了确保请求在传输过程中不会被篡改之外,它还确保了请求被时间限制,有效地防止重放攻击。

可以通过导入以下结构体来提供库本身:

支持API的将返回以下负载,其中至少包含以下信息。

{
    "access_token": "7XF56VIP7ZQQOLGHM6MRIK56S2QS363ULNB5UKNFMJRQVYHQH7IA",
    "refresh_token": "MA2JX5FXWS57DHW4OIHHQDCJVGS3ZKKFCL7XM4GNOB567I6ER4LQ",
    "ikm": "bDEyECRvKKE8w81fX4hz/52cvHsFPMGeJ+a9fGaVvWM=",
    "signing": "7v/CdiGoEI7bcj7R2EyDPH5nrCd2+7rHYNACB+Kf2FMx405und2KenGjNpCBPv0jOiptfHJHiY3lldAQTGCdqw==",
    "expires_at": 1472678411
}

使用此令牌,您可以授权对支持API的以下请求:

use ncryptf::*;

match Token::from_json(json) {
  Err(_) => {},
  Ok(token) => {
    let auth = Authorization::from(
      "POST".to_string(),
      "/api/v1/test".to_string(),
      token,
      chrono::offset::Utc::now(),
      None,
      None
    );

    // make a reqwest with Authorization: auth.get_header()
  }
}

请查阅第1版头部信息的库文档和其他ncryptf库。

加密请求与响应

此库使客户端能够在TLS层之上建立并信任的加密会话,同时(且独立地)提供通过HMAC+HKDF风格的身份验证来认证和识别客户端的能力。

此功能的原因包括但不限于:

  1. 需要额外的安全层
  2. 对网络或TLS本身缺乏信任(参见https://blog.cloudflare.com/incident-report-on-memory-leak-caused-by-cloudflare-parser-bug/
  3. 需要确保为HMAC+HKDF身份验证提供的初始密钥材料(IKM)的机密性
  4. 需要确保向API提交的用户凭证的机密性

您可能想要与API本身建立加密会话的主要原因是为了确保IKM的机密性,以防止在不可信网络上发生数据泄露,避免在Cloudflare-like事件(或任何中间人攻击)中暴露信息。加密会话使您在内存泄露再次发生时能够有信心地使用类似Cloudflare的服务,而不会暴露IKM和其他安全数据。

请参阅test/integration.rs文件以获取请求和加密、解密响应的完整示例。

Rocket.rs体解析,和响应发出

此库提供针对Rocket.rs的特定实现来处理传入的加密(以及通过代理的纯文本)JSON请求。处理此请求的设置如下:

  1. 您的服务器必须提供Redis实例,并且它必须对Rocket可用。

  2. databases配置项添加一个rocket_Db_pools::Config

 let config = rocket::Config::figment()
    .merge(("databases.cache", rocket_db_pools::Config {
        url: format!("redis://127.0.0.1:6379/"),
        min_connections: None,
        max_connections: 1024,
        connect_timeout: 3,
        idle_timeout: None,
    }))
  1. 现在,您可以通过添加一个body接受者来解析和接受ncryptf:🚀:Json的application/json和application/vnd.ncryptf+json,您的请求现在将能够以这种方式解析和接受请求和响应。
#[post("/echo", data="<data>")]
fn echo(data: ncryptf::rocket::Json<TestStruct>) -> ncryptf::rocket::Json<TestStruct> {
    return ncryptf::rocket::Json(data.0);
}

如果需要,也提供了JsonResponse<T>来返回带有状态码的Ncryptf或纯文本JSON响应。

  1. 为了引导,库提供生成引导密钥的便利端点。
/// Define an actual RedisDb instance
#[derive(Database)]
#[database("cache")]
pub struct RedisDb(deadpool_redis::Pool);

/// Use the provided macro to generate the route.
ek_route!(RedisDb);

/// Attach the dynamic macro route to your request.
let rocket = rocket::custom(config)
      .attach(RedisDb::init())
      .mount("/ncryptf", routes![ncryptf_ek_route]);

身份验证和请求验证

Ncryptf还提供了验证请求的功能。虽然tests/rocketts文件夹包含许多针对Rocket的特定示例,但该方法可以用于任何实现。Ncryptf的请求授权类似于AWS Signature V2身份验证,因为它

  1. 为验证服务器端以确认请求未被篡改而签署未加密的请求体。
  2. 防止重放攻击。
  3. 将请求时间限制在签署请求的日期和时间。

客户端应实施以下内容以进行授权请求。

  1. 从IKM端点提供的数据生成令牌。
let token =  ncryptf::Token::from(
        "x2gMeJ5Np0CcKpZav+i9iiXeQBtaYMQ/yeEtcOgY3J".to_string(),
        "LRSEe5zHb1aq20Hr9te2sQF8sLReSkO8bS1eD/9LDM8".to_string(),
        base64::decode("f2mTaH9vkZZQyF7SxVeXDlOSDbVwjUzhdXv2T/YYO8k=").unwrap().to_vec(),
        base64::decode("7v/CdiGoEI7bcj7R2EyDPH5nrCd2+7rHYNACB+Kf2FMx405und2KenGjNpCBPv0jOiptfHJHiY3lldAQTGCdqw==").unwrap().to_vec(),
        now + 14400
    ).unwrap();
  1. 为您的请求创建一个授权对象。
 let auth = match ncryptf::Authorization::from(
        "POST".to_string(), /// this must be uppercase
        "/auth_echo".to_string(),
        token,
        Utc::now(),
        json.clone().to_string(),
        None,
        Some(2)
    ) {
        Ok(auth) => auth,
        Err(_) => {
            assert!(false);
            panic!("unable to generate auth header")
        }
    };
  1. 生成头并添加到请求中。
let client = reqwest::Client::new();
let res = client
    .post("https://www.ncryptf.com/example")
    .header("Authorization:", auth.get_header())
    .send()
    .await?;

如果您已启用client功能,您还可以更简单地执行请求。有关更多详细信息和方法示例,请参阅cargo文档。

Rocket请求保护器

本库额外提供了处理认证请求的功能,包括解析和与Rocket.rs的验证。以下是实现方式。

  1. 让您的用户实体实现ncryptf::rocket::AuthorizationTrait异步特质。
  2. 在您的用户实体实现结束时,运行以下宏来绑定FromRequest特质。
ncryptf::auth!(User);
  1. 您的请求现在可以作为一个Rocket请求保护的一部分检索用户实体。
#[get("/")]
fn auth_echo( _user: User){
    dbg!(_user);
}
  1. 对于带有主体的请求,您可以使用数据保护提取身份和请求数据。
#[post("/", data = "<data>")]
fn auth_echo(data = ncryptf::rocket::RequestData<User>){
    let user: User = data.get_identity();
    let s: ncryptf::rocket::Json::<T> = data.get_data();
}

您可以将此与请求和响应的格式化和解析结合使用。

重要注意事项

该库为了适当读取和解析请求头和主体,对Rocket的几个实现进行了一些奇怪的操作。因此,只有当请求的Content-Type设置为以下任意一种时,授权头才会被解析和处理:application/jsonapplication/vnd.ncryptf+json。通过Accept头处理响应,对于上述两种内容类型,您的Rocket路由必须返回一个ncryptf::rocket::Json<T>,它可以适当处理这两种内容类型。

此外,ncryptf:🚀:Fairing消耗DataStream,直到您的默认JSON限制。如果您的请求开始超过8M的限制,您必须扩展您的限制以捕获整个流,否则所有请求将返回401或403。

由于缺少通用的缓存实现(如PSR-6),该库强制使用Redis。因此,您必须提供一个可用的Redis实例。

V2加密有效载荷

版本2与版本1的有效载荷工作方式相同,区别在于,所有用于解密消息的组件都打包在有效载荷本身中,而不是拆分为单独的头。这减轻了开发者管理多个头的担忧。

版本2的有效载荷描述如下。每个组件都连接在一起。

长度
4字节头DE259002,二进制格式 4字节
nonce 24字节
与私钥关联的公钥 32字节
加密主体 X字节
签名公钥 32字节
签名或原始请求主体 64字节
前述元素的校验和 64字节

依赖项

~9–45MB
~760K SLoC