#zero-knowledge #crypto

no-std dusk-safe

字段元素的Sponge API

4个版本

0.2.1 2024年5月8日
0.2.0 2024年3月27日
0.2.0-rc.02024年3月21日
0.1.0 2024年3月6日

#1157 in 密码学

Download history 1/week @ 2024-04-22 129/week @ 2024-05-06 5/week @ 2024-05-13 43/week @ 2024-05-20 11/week @ 2024-06-03 22/week @ 2024-06-10 13/week @ 2024-06-17 12/week @ 2024-06-24 2/week @ 2024-07-01 10/week @ 2024-07-08 25/week @ 2024-07-15 20/week @ 2024-07-22 18/week @ 2024-07-29 26/week @ 2024-08-05

91 每月下载量
7 个crate中使用(通过dusk-poseidon

MPL-2.0 许可证

36KB
471

SAFE - 字段元素的Sponge API

通用的sponge函数API。

这是一个基于SAFE(字段元素的Sponge API)的sponge函数的最小、no_std、纯Rust实现,用于基于排列的对称原语设计,如哈希函数、MAC、认证加密方案、PRNG等。sponge设计用于在零知识证明系统(ZKPs)以及本地使用,操作任何实现DefaultCopy特质且大小至少为32字节的类型。

介绍

Sponge函数是排列对称原语设计的基石。它们可以被视为一个状态对象,可以在任何时间以任意顺序“吸收”(摄入)输入和“挤压”(产生)输出。

作为其主要特性,此sponge API

  • 不使用任何填充,因此在任何情况下都不会浪费额外的sponge排列调用
  • 独立于底层排列,因此可以与市场上的几乎每一种设计(包括Poseidon的)一起使用。
  • 通过限制在sponge可调用的操作集和将协议设计者绑定到这些操作的具体顺序来消除多种误用模式。
  • 在随机排列模型中,在包括被忽视但经常需要跨协议安全在内的多种设置中是可证明安全的。
  • 是第一个将协议的元数据存储在sponge内部部分的结构之一,可证明不会损失安全性

此sponge构造本身不支持可变长度的哈希,即预先不知道要哈希的数据长度的哈希。然而,可以通过在hasher中包装sponge来实现这种行为,该hasher仅在最终确定哈希时开始sponge,因此是在已知输入长度的时间(例如,此包装器的示例实现可以在dusk-poseidon中找到)。

构造

此库中构建的sponge由以下定义

  • 一个类型为 [T; W] 的状态,其中 T 类型为 T: Default + Copy,宽度为 W
  • 一个排列函数,用于排列状态
  • 容量为 1
  • 速率为 R,其中 R = W - 1
  • 一个输入输出(IO)模式,定义了摄入 len 项输入(absorb(len))和产生输出(squeeze(len))的序列(例如:[absorb(4), absorb(1), squeeze(3)]
  • 一个域分隔符,用于区分具有不同用例的等效海绵。

注意:由于容量是类型 T 的一个元素,我们需要将 T 限制为至少 256 位。将不同大小的输入正确序列化为至少 256 位的类型是用户的责任。

抽象 API

开始

  1. 验证 IO 模式
    • IO 模式至少有两个调用。
    • 第一个调用是到 absorb
    • 最后一个调用是到 squeeze
    • 没有调用有 len == 0
  2. 使用 IO 模式和域分隔符计算标签。
    1. 将 IO 模式编码为一个 32 位字的列表,对于 absorb 将 MSB 设置为 1,对于 squeeze 设置为 0,并将长度添加到低比特位。任何连续的 absorbsqueeze 调用将被聚合,例如,上面的 IO 模式示例 [absorb(4), absorb(1), squeeze(3)] 的编码与 [absorb(5), squeeze(3)] 相同:[0x8000_0005, 0x0000_0001]
    2. 将字列表序列化为字节字符串,并将其域分隔符附加到其后:例如,如果域分隔符编码设置为两个字节的序列 0x4142,则上面的示例将产生以下字符串(使用大端序):0x80000005000000014142
    3. 将字节字符串哈希到标签,标签是类型 T 的一个元素。
  3. 将状态的第一元素初始化为标签,其余元素设置为默认值 T
  4. 将吸收和压缩位置都设置为零。
  5. 将IO计数设置为零。

完成

  1. 如果IO计数等于IO模式的长度,则返回输出向量;否则返回错误。
  2. 清除状态及其变量

吸收(长度,输入)

  1. 检查吸收调用是否与IO模式中在IO计数处的条目匹配,并检查输入是否提供足够的元素(如果不够,则清除状态并返回错误)。
  2. 对于 input 的前 len 个元素
    1. 如果 pos_absorb == rate,则调用排列函数,并将 pos_absorb = 0
    2. 将元素添加到状态中的 pos_absort + 1 位置(我们跳过了第一个元素,因为它是容量)。
    3. pos_absorb 增加一。
  3. 增加IO计数。
  4. pos_squeeze 设置为速率,以强制在下一个调用 squeeze 时的开始调用排列函数。

压缩(长度)

  1. 检查吸收调用是否与IO模式中在IO计数处的条目匹配(如果不符合,则清除状态并返回错误)。
  2. 重复 len
    1. 如果 pos_squeeze == rate,则调用排列函数,并将 pos_squeeze = 0
    2. 将状态中位置 pos_squeeze + 1 的元素追加到输出向量中(同样,由于容量,我们也跳过了第一个元素)。
  3. 增加IO计数。

注意,我们不会像在 absorb 调用中对 pos_squeeze 那样将 pos_absorb 设置为速率,因为这可能是因为我们想要状态在压缩过的相同位置吸收。

示例

use dusk_bls12_381::BlsScalar;
use ff::Field;
use rand::rngs::StdRng;
use rand::SeedableRng;
use dusk_safe::{Error, Call, Safe, Sponge};

const W: usize = 7;

#[derive(Default, Debug, Clone, Copy, PartialEq)]
struct Rotate();

impl Safe<BlsScalar, W> for Rotate {
    // Rotate every item one item to the left, first item becomes last.
    // Note: This permutation is just an example and *should not* be used for a
    // sponge construction for cryptographically safe hash functions.
    fn permute(&mut self, state: &mut [BlsScalar; W]) {
        let tmp = state[0];
        for i in 1..W {
            state[i - 1] = state[i];
        }
        state[W - 1] = tmp;
    }

    // Define the hasher used to generate the tag from the encoding of the
    // io-pattern and domain-separator.
    fn tag(&mut self, input: &[u8]) -> BlsScalar {
        BlsScalar::hash_to_scalar(input)
    }

    fn add(&mut self, right: &BlsScalar, left: &BlsScalar) -> BlsScalar {
        right + left
    }
}

impl Rotate {
    pub fn new() -> Self {
        Self()
    }
}

// pick a domain-separator
let domain_sep = 0;

// generate random input
let mut rng = StdRng::seed_from_u64(0x42424242);
let mut input = [BlsScalar::zero(); 8];
input.iter_mut().for_each(|s| *s = BlsScalar::random(&mut rng));

// build the io-pattern
let iopattern = vec![
    Call::Absorb(6),
    Call::Absorb(2),
    Call::Squeeze(1),
    Call::Squeeze(2),
];

// start the sponge
let mut sponge = Sponge::start(
    Rotate::new(),
    iopattern,
    domain_sep,
)
.expect("io-pattern should be valid");

// absorb 6 elements
sponge.absorb(6, &input).expect("absorbing should not fail");
// absorb 2 elements
sponge.absorb(2, &input[6..]).expect("absorbing should not fail");

// squeeze 1 element
sponge.squeeze(1).expect("squeezing should not fail");
// squeeze 2 elements
sponge.squeeze(2).expect("squeezing should not fail");

// generate the hash output
let output1 = sponge.finish().expect("Finishing should not fail");


// Generate another hash output from the same input and aggregated IO pattern:
// build the io-pattern
let iopattern = vec![
    Call::Absorb(8),
    Call::Squeeze(3),
];

// start the sponge
let mut sponge = Sponge::start(
    Rotate::new(),
    iopattern,
    domain_sep,
)
.expect("io-pattern should be valid");

// absorb 8 elements
sponge.absorb(8, &input).expect("absorbing should not fail");

// squeeze 3 elements
sponge.squeeze(3).expect("squeezing should not fail");

// generate the hash output
let output2 = sponge.finish().expect("Finishing should not fail");

assert_eq!(output1, output2);

依赖关系

~32KB