Skip to main content

rhdl_ed25519_fast_core/
scalar.rs

1//! Minimal scalar representation and primitives used by the fast backend.
2//!
3//! This module deliberately contains only the operations required by the
4//! fixed-64 reducer and recoder. Keeping them here makes the hardware backend a
5//! self-contained crate without pulling in the older serial scalar engine.
6
7use rhdl::prelude::*;
8
9/// A scalar represented as four little-endian 64-bit words.
10#[derive(Copy, Clone, Debug, PartialEq, Digital)]
11pub struct ScalarWords {
12    /// Little-endian words; word zero carries bits 63 through 0.
13    pub words: [b64; 4],
14}
15
16/// Return scalar zero in synthesizable code.
17#[kernel]
18pub fn scalar_zero_kernel(_unused: bool) -> ScalarWords {
19    ScalarWords { words: [b64(0); 4] }
20}
21
22/// Return the Ed25519 group order `l` in synthesizable code.
23#[kernel]
24pub fn scalar_l_kernel(_unused: bool) -> ScalarWords {
25    ScalarWords {
26        words: [
27            b64(0x5812631a5cf5d3ed),
28            b64(0x14def9dea2f79cd6),
29            b64(0x0000000000000000),
30            b64(0x1000000000000000),
31        ],
32    }
33}
34
35/// Compare two four-word unsigned values.
36#[kernel]
37pub fn scalar_ge_kernel(a: ScalarWords, b: ScalarWords) -> bool {
38    let mut greater = false;
39    let mut equal = true;
40    for index in 0..4 {
41        let word = 3 - index;
42        if equal && a.words[word] > b.words[word] {
43            greater = true;
44            equal = false;
45        } else if equal && a.words[word] < b.words[word] {
46            equal = false;
47        }
48    }
49    greater || equal
50}
51
52/// Subtract two four-word values with propagated borrow.
53#[kernel]
54pub fn scalar_sub_kernel(a: ScalarWords, b: ScalarWords) -> ScalarWords {
55    let mut output = scalar_zero_kernel(false);
56    let mut borrow = false;
57    for index in 0..4 {
58        let borrow_word = if borrow { b64(1) } else { b64(0) };
59        output.words[index] = a.words[index] - b.words[index] - borrow_word;
60        borrow = (a.words[index] < b.words[index]) || (borrow && a.words[index] == b.words[index]);
61    }
62    output
63}
64
65/// Shift a four-word value left and insert one least-significant bit.
66#[kernel]
67pub fn scalar_shift_in_kernel(value: ScalarWords, bit: bool) -> ScalarWords {
68    let mut output = scalar_zero_kernel(false);
69    output.words[0] = (value.words[0] << 1) | if bit { b64(1) } else { b64(0) };
70    output.words[1] = (value.words[1] << 1) | (value.words[0] >> 63);
71    output.words[2] = (value.words[2] << 1) | (value.words[1] >> 63);
72    output.words[3] = (value.words[3] << 1) | (value.words[2] >> 63);
73    output
74}