Skip to main content

rhdl_ubft/cert/
mod.rs

1//! Certificates (W4; docs/rhdl_ubft-view-change.md): `q = f + 1` signature
2//! shares from distinct replicas over one exact key `(role, view, instance,
3//! digest)`.
4//!
5//! Two encodings are fixed here, as kernels, so hardware and tests agree:
6//! - [`value_block`]: the 128-byte SHA-512 input naming a value,
7//!   `"RUB2-VAL" ‖ session ‖ instance ‖ len ‖ 0^8 ‖ value`, zero past `len`.
8//!   The value digest `d` is its first 32 bytes. The session is inside
9//!   `d`, so a share never transfers between runs.
10//! - [`share_context`]: the 48 signed bytes (sign layout v2):
11//!   `"RUB2" ‖ role ‖ 0 0 0 ‖ view ‖ instance ‖ d`. View and instance are
12//!   full 32-bit fields (upstream packs a 16-bit view into the index,
13//!   UE-C7), and the role separates prepare shares from history shares.
14use rhdl::prelude::*;
15
16use crate::ctb::bswap64;
17
18pub mod collector;
19
20/// Role byte of a prepare (certify) share.
21pub const ROLE_PREPARE: u128 = 3;
22/// Role byte of a sealed-history share.
23pub const ROLE_HISTORY: u128 = 4;
24/// `"RUB2-VAL"` as a little-endian word.
25pub const VAL_DOMAIN: u128 = 0x4C41_562D_3242_5552;
26
27/// The 128-byte value-digest input as 16 big-endian SHA-512 words.
28#[kernel]
29pub fn value_block<const P: usize>(session: b64, instance: b32, len: b5, value: [b32; P]) -> [b64; 16] {
30    let mut le = [b64(0); 16];
31    le[0] = b64(VAL_DOMAIN);
32    le[1] = session;
33    le[2] = instance.resize::<64>() | (len.resize::<64>() << b64(32));
34    for k in 0..P {
35        let word = if b5(k as u128) < len { value[k] } else { b32(0) };
36        let w = (k >> 1) + 4;
37        let sh = (k & 1) * 32;
38        le[w] = le[w] | (word.resize::<64>() << b64(sh as u128));
39    }
40    let mut be = [b64(0); 16];
41    for k in 0..16 {
42        be[k] = bswap64(le[k]);
43    }
44    be
45}
46
47/// The value digest from a finished SHA-512 state: bytes 0..32 as
48/// little-endian words.
49#[kernel]
50pub fn value_digest(state: [b64; 8]) -> [b64; 4] {
51    let mut d = [b64(0); 4];
52    for k in 0..4 {
53        d[k] = bswap64(state[k]);
54    }
55    d
56}
57
58/// The 48 signed bytes of a share, as six little-endian words.
59#[kernel]
60pub fn share_context(role: b8, view: b32, instance: b32, digest: [b64; 4]) -> [b64; 6] {
61    let mut c = [b64(0); 6];
62    c[0] = b64(0x3242_5552) | (role.resize::<64>() << b64(32));
63    c[1] = view.resize::<64>() | (instance.resize::<64>() << b64(32));
64    c[2] = digest[0];
65    c[3] = digest[1];
66    c[4] = digest[2];
67    c[5] = digest[3];
68    c
69}