7个版本 (重大变更)

0.5.0 2024年2月4日
0.4.0 2022年10月27日
0.3.0 2022年5月2日
0.2.1 2021年12月27日
0.0.0 2020年2月9日

#941加密学

Download history 41/week @ 2024-04-08 152/week @ 2024-04-15 33/week @ 2024-04-22 39/week @ 2024-04-29 34/week @ 2024-05-06 52/week @ 2024-05-13 60/week @ 2024-05-20 32/week @ 2024-05-27 43/week @ 2024-06-03 82/week @ 2024-06-10 37/week @ 2024-06-17 40/week @ 2024-06-24 164/week @ 2024-07-01 223/week @ 2024-07-08 496/week @ 2024-07-15 367/week @ 2024-07-22

每月1,256次下载
6crate中使用 5 直接

MIT/Apache

91KB
1.5K SLoC

age-plugin Rust库

此crate提供了一个构建age插件的API。

简介

age文件加密格式遵循“一个精炼的联合”设计理念。在特定格式版本内的扩展机制是age头部中的接收者段落:文件密钥可以用任何方式包装,并且age客户端需要忽略他们不理解的部分。

执行此机制的核心API包括

  • 一个包装文件密钥并返回段落的接收者。
  • 一个解包段落并返回文件密钥的身份。

age插件系统提供了一个机制,用于在进程边界上公开这些核心API。它有两个主要组件

  • 将接收者和身份映射到插件二进制文件的映射。
  • 包装和解包文件密钥的状态机。

使用这种可组合的设计,您可以实现可以直接与age库crate一起使用的接收者或身份,并将其作为插件二进制文件部署以供rage等客户端使用。

将接收者和身份映射到插件二进制文件

age插件通过任意不区分大小写的字符串NAME进行标识。此字符串在三个地方使用

  • 兼容插件的接收者使用带有HRP age1name(小写)的Bech32进行编码。
  • 兼容插件的身份使用带有HRP AGE-PLUGIN-NAME-(大写)的Bech32进行编码。
  • 插件二进制文件(由age客户端启动)的名称为age-plugin-name

用户通过提供文件加密的接收者或文件解密的身份与age客户端交互。当提供插件接收者或身份时,age客户端将搜索对应插件名称的二进制文件所在的PATH

接收者段落类型不需要与特定插件名称相关联。在解密时,age客户端将传递所有接收者段落到每个连接的插件。插件必须忽略他们不知道的段落。

插件二进制可以通过在多个名称下出现在PATH中来处理多个收件人或身份类型。这可以通过到规范二进制的符号链接或别名来实现。

多个插件二进制可以支持相同的收件人和身份类型;age客户端将使用在PATH中找到的第一个二进制文件。一些Unix操作系统支持"alternatives",插件二进制如果提供对常见收件人或身份类型的支持,应利用这一功能。

请注意,用户指定的身份不需要指向特定的解密密钥,或者实际上不包含任何密钥材料。它只需要包含足够的信息,以便插件能够定位必要的密钥材料。

标准age密钥

插件可以支持解密加密为本地age收件人的文件,通过包括对x25519收件人段落的支持。此类插件将选择自己的名称,用户将使用包含指定该插件名称的身份文件。

示例插件二进制

以下示例使用clap来解析命令行参数,但任何可以检测到--age-plugin=STATE_MACHINE标志的参数解析逻辑都将有效。

use age_core::format::{FileKey, Stanza};
use age_plugin::{
    identity::{self, IdentityPluginV1},
    print_new_identity,
    recipient::{self, RecipientPluginV1},
    Callbacks, run_state_machine,
};
use clap::Parser;

use std::collections::HashMap;
use std::io;

struct RecipientPlugin;

impl RecipientPluginV1 for RecipientPlugin {
    fn add_recipient(
        &mut self,
        index: usize,
        plugin_name: &str,
        bytes: &[u8],
    ) -> Result<(), recipient::Error> {
        todo!()
    }

    fn add_identity(
        &mut self,
        index: usize,
        plugin_name: &str,
        bytes: &[u8]
    ) -> Result<(), recipient::Error> {
        todo!()
    }

    fn wrap_file_keys(
        &mut self,
        file_keys: Vec<FileKey>,
        mut callbacks: impl Callbacks<recipient::Error>,
    ) -> io::Result<Result<Vec<Vec<Stanza>>, Vec<recipient::Error>>> {
        todo!()
    }
}

struct IdentityPlugin;

impl IdentityPluginV1 for IdentityPlugin {
    fn add_identity(
        &mut self,
        index: usize,
        plugin_name: &str,
        bytes: &[u8]
    ) -> Result<(), identity::Error> {
        todo!()
    }

    fn unwrap_file_keys(
        &mut self,
        files: Vec<Vec<Stanza>>,
        mut callbacks: impl Callbacks<identity::Error>,
    ) -> io::Result<HashMap<usize, Result<FileKey, Vec<identity::Error>>>> {
        todo!()
    }
}

#[derive(Debug, Parser)]
struct PluginOptions {
    #[arg(help = "run the given age plugin state machine", long)]
    age_plugin: Option<String>,
}

fn main() -> io::Result<()> {
    let opts = PluginOptions::parse();

    if let Some(state_machine) = opts.age_plugin {
        // The plugin was started by an age client; run the state machine.
        run_state_machine(
            &state_machine,
            Some(|| RecipientPlugin),
            Some(|| IdentityPlugin),
        )?;
        return Ok(());
    }

    // Here you can assume the binary is being run directly by a user,
    // and perform administrative tasks like generating keys.

    Ok(())
}

许可证

根据您的选择,许可协议为:

任选其一。

贡献

除非您明确声明,否则根据Apache-2.0许可证定义,您提交的任何有意包含在工作中的贡献都将按照上述方式双许可,无需任何附加条款或条件。

依赖关系

~5–14MB
~164K SLoC