恒定时间加密验证技能Skill multiversx-constant-time

这个技能用于验证加密操作是否在恒定时间内执行,以防止时序攻击。主要应用于审计自定义加密实现、秘密比较逻辑、认证验证代码等,在MultiversX平台上的智能合约安全分析中尤为重要。关键词:加密安全、恒定时间、时序攻击、审计、MultiversX、智能合约、密码学、安全验证。

智能合约 0 次安装 0 次浏览 更新于 3/21/2026

name: multiversx-constant-time description: 验证加密操作在恒定时间内执行以防止时序攻击。在审计自定义加密实现、秘密比较或智能合约中的安全敏感算法时使用。

恒定时间分析

验证加密秘密是否在恒定时间内处理,以防止时序攻击。此技能对于审查任何处理敏感数据的代码至关重要,其中执行时间可能泄露信息。

何时使用

  • 审计自定义加密实现
  • 审查秘密比较逻辑(哈希、签名、密钥)
  • 分析认证或验证代码
  • 检查密码/PIN处理
  • 审查任何时序可能泄露秘密的代码

1. 理解时序攻击

威胁模型

攻击者测量操作所需时间来推断秘密值:

比较: secret[i] == input[i]
- 如果不匹配在 i=0: ~100ns (立即返回)
- 如果不匹配在 i=5: ~150ns (先检查5字节)
- 如果全部匹配: ~200ns (检查所有字节)

攻击: 尝试字节0的所有值,找到最快拒绝的 = 错误猜测
        对每个字节位置重复

为什么在MultiversX上重要

  • Gas计量可能泄露执行路径信息
  • 跨分片时序差异可观测
  • VM级优化可能改变执行时间

2. 避免的模式(可变时间)

早期退出比较

// 易受攻击: 早期退出泄露第一个不匹配的位置
fn compare_secrets(secret: &[u8], input: &[u8]) -> bool {
    if secret.len() != input.len() {
        return false;  // 长度泄露!
    }
    for i in 0..secret.len() {
        if secret[i] != input[i] {
            return false;  // 位置泄露!
        }
    }
    true
}

短路布尔运算符

// 易受攻击: && 和 || 短路
fn verify_auth(token_valid: bool, signature_valid: bool) -> bool {
    token_valid && signature_valid  // 如果 token_valid 为假,签名不被检查
}

基于秘密的条件分支

// 易受攻击: 基于秘密值的不同代码路径
fn process_key(key: &[u8]) {
    if key[0] == 0x00 {
        // 快速路径
    } else {
        // 慢速路径,有更多操作
    }
}

数据依赖的内存访问

// 易受攻击: 基于秘密值的缓存时序
fn lookup(secret_index: usize, table: &[u8]) -> u8 {
    table[secret_index]  // 缓存命中/未命中取决于 secret_index
}

3. MultiversX安全解决方案

使用VM加密函数

最佳实践: 总是优先使用内置VM加密操作:

// 正确: 使用VM提供的验证
fn verify_signature(&self, message: &ManagedBuffer, signature: &ManagedBuffer) {
    let signer = self.expected_signer().get();
    // 如果验证失败,用信号错误panic — 不返回布尔值
    self.crypto().verify_ed25519(
        signer.as_managed_buffer(),
        message,
        signature
    );
}

// 正确: 使用VM提供的哈希
fn hash_data(&self, data: &ManagedBuffer) -> ManagedBuffer {
    self.crypto().sha256(data)
}

ManagedBuffer比较

MultiversX VM的ManagedBuffer比较通常是恒定时间的:

// 正确: ManagedBuffer == 使用VM比较
fn verify_hash(&self, input_hash: &ManagedBuffer) -> bool {
    let stored_hash = self.secret_hash().get();
    stored_hash == *input_hash  // VM处理比较
}

手动恒定时间比较(必要时)

如果必须比较原始字节:

// 正确: 恒定时间字节比较
fn constant_time_compare(a: &[u8], b: &[u8]) -> bool {
    if a.len() != b.len() {
        return false;
    }

    let mut result: u8 = 0;
    for i in 0..a.len() {
        result |= a[i] ^ b[i];  // 累积差异
    }
    result == 0  // 一次性检查所有
}

使用subtle板条箱

对于需要恒定时间操作的Rust代码:

use subtle::ConstantTimeEq;

fn verify_secret(stored: &[u8; 32], provided: &[u8; 32]) -> bool {
    stored.ct_eq(provided).into()  // 恒定时间比较
}

注意: 验证subtle板条箱与no_std和WASM兼容。

4. 验证技术

代码审查清单

  • [ ] 没有基于秘密比较的早期返回
  • [ ] 没有使用秘密依赖操作数的&&||
  • [ ] 没有基于秘密值的分支(if/match
  • [ ] 没有使用秘密索引的数组索引
  • [ ] 使用VM加密函数(如果可用)

静态分析模式

搜索潜在易受攻击的模式:

# 在比较类函数中查找早期返回
grep -n "return false" src/*.rs | grep -i "compare\|verify\|check"

# 查找敏感名称的短路运算符
grep -n "&&\|\\|\\|" src/*.rs | grep -i "secret\|key\|hash\|signature"

# 查找常见秘密变量名的条件分支
grep -n "if.*secret\|if.*key\|if.*hash" src/*.rs

Gas分析

在MultiversX上,Gas消耗可能指示时序:

// 检查Gas是否随输入变化
#[view]
fn gas_test(&self, input: ManagedBuffer) -> u64 {
    let before = self.blockchain().get_gas_left();
    // ... 要测试的操作 ...
    let after = self.blockchain().get_gas_left();
    before - after
}

警告: 这是近似的。真正的恒定时间需要VM级保证。

5. 常见易受攻击场景

认证令牌验证

// 易受攻击
fn verify_token(&self, token: &ManagedBuffer) -> bool {
    let valid_token = self.auth_token().get();
    let mut token_bytes = [0u8; 64];
    let mut valid_bytes = [0u8; 64];
    token.load_slice(0, &mut token_bytes[..token.len()]);
    valid_token.load_slice(0, &mut valid_bytes[..valid_token.len()]);
    for i in 0..token.len() {
        if token_bytes[i] != valid_bytes[i] {
            return false;  // 时序泄露!
        }
    }
    true
}

// 正确
fn verify_token(&self, token: &ManagedBuffer) -> bool {
    let valid_token = self.auth_token().get();
    valid_token == *token  // ManagedBuffer相等性
}

HMAC验证

// 易受攻击: 在计算的HMAC上使用 ==
fn verify_hmac(&self, message: &ManagedBuffer, provided_mac: &ManagedBuffer) -> bool {
    let computed_mac = self.compute_hmac(message);
    computed_mac == *provided_mac  // 可能可变时间!
}

// 正确: 使用VM加密或恒定时间比较
fn verify_hmac(&self, message: &ManagedBuffer, provided_mac: &ManagedBuffer) -> bool {
    let computed_mac = self.compute_hmac(message);
    self.constant_time_eq(&computed_mac, provided_mac)
}

密码/PIN比较

// 易受攻击
fn check_pin(&self, entered_pin: u32) -> bool {
    entered_pin == self.stored_pin().get()  // 比较可能短路
}

// 正确: 总是比较所有位
fn check_pin(&self, entered_pin: u32) -> bool {
    let stored = self.stored_pin().get();
    (entered_pin ^ stored) == 0  // XOR和检查
}

6. 审计报告模板

## 恒定时间分析

### 范围
审查的文件: [列表]
发现的加密操作: [计数]

### 发现

| 位置 | 操作 | 状态 | 备注 |
|----------|-----------|--------|-------|
| lib.rs:45 | 哈希比较 | 安全 | 使用 ManagedBuffer == |
| auth.rs:23 | 令牌验证 | 易受攻击 | 早期返回模式 |
| crypto.rs:89 | 签名 | 安全 | 使用 self.crypto() |

### 建议
1. [每个易受攻击位置的具体修复]

7. 关键原则

  1. 优先VM函数: self.crypto().*方法被优化,可能是恒定时间的
  2. 避免DIY加密: 自定义实现很少必要且经常错误
  3. 假设时序泄露: 任何基于秘密的分支都是潜在漏洞
  4. 用Gas测试: Gas消耗可能揭示时序变化
  5. 文档假设: 注意哪些操作你假设是恒定时间的