Skip to main content

rhdl_ubft/crypto/
ed25519.rs

1//! Ed25519 for the slow path: this repository's uBFT service behind
2//! rhdl_ubft's crypto ports.
3//!
4//! [`Ed25519Crypto<NV>`] is the service `UbftEd25519<1, NV>` (the measured
5//! small signer, `NV` known-key verify machines, 16 key slots) with the
6//! [`Setup`](crate::setup::Setup) sequencer as its host: at `setup` it loads
7//! the seed and every replica's key and starts the service. The streams pass
8//! straight through; only the beat layout changes (the service carries
9//! bytes, the controller eight 64-bit lanes).
10
11use rhdl::prelude::*;
12use rhdl_ed25519_scalable_core::ubft::{self as svc, UbftEd25519, beat_words, words_beat};
13
14use crate::ports::{AxiLiteOut, Axis64, Axis512, CryptoIn, CryptoOut, CryptoScheme};
15use crate::setup::{ED25519_MAP, Setup, SetupIn};
16
17/// Ed25519 behind the crypto ports.
18#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
19pub struct Ed25519Crypto<const NV: usize> {
20    setup: Setup,
21    service: UbftEd25519<1, NV>,
22    ready: rhdl_fpga::core::dff::DFF<bool>,
23    /// Sticky framing fault (R-10): a sign beat that is not one full beat
24    /// (`last` set, every `keep` bit set), or a verify beat with a partial
25    /// `keep`, was refused. From then on nothing more reaches the service.
26    framing: rhdl_fpga::core::dff::DFF<bool>,
27}
28
29impl<const NV: usize> Default for Ed25519Crypto<NV> {
30    fn default() -> Self {
31        Self { setup: Setup::default(), service: UbftEd25519::default(), ready: rhdl_fpga::core::dff::DFF::new(false), framing: rhdl_fpga::core::dff::DFF::new(false) }
32    }
33}
34
35impl<const NV: usize> CryptoScheme for Ed25519Crypto<NV> {
36    const SHARE_BEATS: usize = 1;
37    const SCHEME: u8 = 1;
38}
39
40impl<const NV: usize> SynchronousIO for Ed25519Crypto<NV> {
41    type I = CryptoIn;
42    type O = CryptoOut;
43    type Kernel = ed25519_crypto_kernel<NV>;
44}
45
46/// Controller beat → service beat.
47#[kernel]
48pub fn to_service(b: Axis512) -> svc::Axis512 {
49    svc::Axis512 { valid: b.valid, data: words_beat(b.data), last: b.last }
50}
51
52/// Service beat → controller beat.
53#[kernel]
54pub fn from_service(b: svc::Axis512) -> Axis512 {
55    Axis512 { valid: b.valid, data: beat_words(b.data), keep: !b64(0), last: b.last }
56}
57
58/// R-10: which presented beats the service would misread. A sign request is
59/// exactly one beat with every byte kept; a verify beat keeps every byte
60/// (the service checks the verify record's beat count and `last`).
61#[kernel]
62pub fn frame_errors(sign: Axis512, verify: Axis512) -> (bool, bool) {
63    let sign_bad = sign.valid && (!sign.last || sign.keep != !b64(0));
64    let verify_bad = verify.valid && verify.keep != !b64(0);
65    (sign_bad, verify_bad)
66}
67
68/// The wiring.
69#[kernel]
70pub fn ed25519_crypto_kernel<const NV: usize>(
71    _cr: ClockReset,
72    i: CryptoIn,
73    q: Ed25519CryptoQ<NV>,
74) -> (CryptoOut, Ed25519CryptoD<NV>) {
75    let su = q.setup;
76    let sv = q.service;
77    // Setup is the service's host.
78    let mut d = Ed25519CryptoD::<NV>::dont_care();
79    d.setup = SetupIn {
80        start: i.setup,
81        map: ED25519_MAP,
82        n: i.n,
83        sk: i.sk,
84        keys_base: i.keys_base,
85        svc: AxiLiteOut {
86            aw_ready: sv.axi.aw_ready,
87            w_ready: sv.axi.w_ready,
88            b_valid: sv.axi.b_valid,
89            b_resp: sv.axi.b_resp,
90            ar_ready: sv.axi.ar_ready,
91            r_valid: sv.axi.r_valid,
92            r_data: sv.axi.r_data,
93            r_resp: sv.axi.r_resp,
94            interrupt: sv.axi.interrupt,
95        },
96        msg: i.msg,
97    };
98    // Frames the service would misread are refused here and the fault is
99    // sticky: a sign request must be exactly one beat with every byte kept;
100    // verify beats must keep every byte (their count and `last` are checked
101    // by the service).
102    let (sign_bad, verify_bad) = frame_errors(i.sign, i.verify);
103    let framing = q.framing || sign_bad || verify_bad;
104    d.framing = framing;
105    if i.setup {
106        d.framing = false;
107    }
108    let pass = !framing;
109    let mut sign_in = to_service(i.sign);
110    sign_in.valid = i.sign.valid && pass;
111    let mut verify_in = to_service(i.verify);
112    verify_in.valid = i.verify.valid && pass;
113    let a = su.svc;
114    d.service = svc::UbftIn {
115        axi: svc::AxiLiteIn {
116            aw_valid: a.aw_valid,
117            aw_addr: a.aw_addr,
118            w_valid: a.w_valid,
119            w_data: a.w_data,
120            w_strb: a.w_strb,
121            b_ready: a.b_ready,
122            ar_valid: false,
123            ar_addr: b12(0),
124            r_ready: true,
125        },
126        sign: sign_in,
127        sig_ready: i.sig_ready,
128        verify: verify_in,
129        verdict_ready: i.verdict_ready,
130    };
131    let mut ready = q.ready;
132    if i.setup {
133        ready = false;
134    }
135    if su.done {
136        ready = true;
137    }
138    d.ready = ready;
139    let o = CryptoOut {
140        ready: q.ready,
141        msg: su.msg,
142        // Refused beats are consumed (never stalled), so the controller
143        // never hangs on them.
144        sign_ready: sv.sign_ready || !pass,
145        sig: from_service(sv.sig),
146        verify_ready: sv.verify_ready || !pass,
147        verdict: Axis64 { valid: sv.verdict.valid, data: sv.verdict.data },
148        fault: q.framing,
149        announce: Axis512 { valid: false, data: [b64(0); 8], keep: b64(0), last: false },
150        cert_ready: true,
151    };
152    (o, d)
153}