Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,15 @@ jobs:

- name: Run unit tests
run: scarb test

build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Scarb
uses: software-mansion/[email protected]

- name: Build packages
run: scarb build
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
scarb 2.11.4
scarb nightly-2025-06-07
80 changes: 43 additions & 37 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
TARGET_DIR = target

install-stwo:
# NOTE: rust-toolchain.toml must be the same as the one in the stwo-cairo repo
install-cairo-prove:
RUSTFLAGS="-C target-cpu=native -C opt-level=3" \
cargo install \
--git https://github.com/starkware-libs/stwo-cairo \
--rev 61d338ee93f11a735eb5cd86f024f7a73d59d420 \
adapted_stwo

install-cairo-execute:
cargo install --git https://github.com/ohad-agadi/cairo.git --rev 24c4130 cairo-execute
--git https://github.com/starkware-libs/stwo-cairo \
--rev adc68829b0e913d5a8bdf14932a45fde27a2e335 \
cairo-prove

falcon-execute:
rm -rf $(TARGET_DIR)/execute/falcon \
Expand All @@ -23,34 +19,44 @@ falcon-args:
falcon-build:
scarb --profile release build --package falcon

falcon-cairo-execute:
rm -rf $(TARGET_DIR)/execute/falcon \
&& mkdir -p $(TARGET_DIR)/execute/falcon/execution1 \
&& cairo-execute \
--layout all_cairo \
--args-file packages/falcon/tests/data/args_512_1.json \
--standalone \
--disable-trace-padding true \
--prebuilt \
--trace-file $(TARGET_DIR)/execute/falcon/execution1/trace.bin \
--memory-file $(TARGET_DIR)/execute/falcon/execution1/memory.bin \
--air-public-input $(TARGET_DIR)/execute/falcon/execution1/air_public_input.json \
--air-private-input $(TARGET_DIR)/execute/falcon/execution1/air_private_input.json \
$(TARGET_DIR)/release/falcon.executable.json

falcon-prove:
adapted_stwo \
--priv_json $(TARGET_DIR)/execute/falcon/execution1/air_private_input.json \
--pub_json $(TARGET_DIR)/execute/falcon/execution1/air_public_input.json \
--proof_path $(TARGET_DIR)/proof.json \
--params_json prover_params.json \
--verify
falcon-prove: falcon-build
rm -rf $(TARGET_DIR)/execute/falcon
mkdir -p $(TARGET_DIR)/execute/falcon
cairo-prove prove \
$(TARGET_DIR)/release/falcon.executable.json \
$(TARGET_DIR)/execute/falcon/proof.json \
--arguments-file packages/falcon/tests/data/args_512_1.json \
--proof-format cairo-serde

falcon-burn:
scarb burn --package falcon --arguments-file packages/falcon/tests/data/args_512_1.json --output-file target/falcon.svg --open-in-browser

sphincs-execute:
scarb --profile release execute --package sphincs_plus --print-resource-usage --arguments-file packages/sphincs-plus/tests/data/sha2_simple_128s.json

sphincs-burn:
scarb burn --package sphincs_plus --output-file target/sphincs-plus.svg --open-in-browser
scarb burn --package falcon \
--arguments-file packages/falcon/tests/data/args_512_1.json \
--output-file target/falcon.svg \
--open-in-browser

sphincs-build:
scarb --profile release build --package sphincs_plus --features blake_hash,sparse_addr

sphincs-execute: sphincs-build
rm -rf $(TARGET_DIR)/execute/sphincs_plus
scarb --profile release execute \
--no-build \
--package sphincs_plus \
--print-resource-usage \
--arguments-file packages/sphincs-plus/tests/data/sha2_simple_128s.json

sphincs-burn: sphincs-build
scarb burn --package sphincs_plus \
--no-build \
--output-file target/sphincs-plus.svg \
--arguments-file packages/sphincs-plus/tests/data/sha2_simple_128s.json \
--open-in-browser

sphincs-prove: sphincs-build
rm -rf $(TARGET_DIR)/execute/sphincs_plus
mkdir -p $(TARGET_DIR)/execute/sphincs_plus
cairo-prove prove \
$(TARGET_DIR)/release/sphincs_plus.executable.json \
$(TARGET_DIR)/execute/sphincs_plus/proof.json \
--arguments-file packages/sphincs-plus/tests/data/sha2_simple_128s.json \
--proof-format cairo-serde
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Implementation details:
- [ ] Stwo proving benchmarks

Follow-up:
- [ ] Sphincs+ 128s with Blake2s and 4-byte aligned address encoding
- [x] Sphincs+ 128s with Blake2s and 4-byte aligned address encoding
- [ ] Falcon512 with probabilistic polynomial multiplication checking

## References
Expand Down
1 change: 0 additions & 1 deletion Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ members = ["packages/*"]

[cairo]
enable-gas = false
sierra-replace-ids = true
2 changes: 1 addition & 1 deletion packages/falcon/src/falcon.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ mod tests {
8300, 3298, 9483, 5987, 12127, 7279, 8021, 12123, 12011, 8915, 676, 7129, 11601, 1593,
10526, 9038, 3417, 10657, 4936, 5525,
];
if let Err(e) = verify_uncompressed(s1.span(), pk.span(), msg_point.span(), 1024) {
if let Err(e) = verify_uncompressed::<1024>(s1.span(), pk.span(), msg_point.span(), 1024) {
println!("Error: {:?}", e);
assert!(false);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/falcon/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn main(args: Args) {
println!("Verifying {} signatures", attestations.len());

for attestation in attestations {
falcon::verify_uncompressed(attestation.s1, attestation.pk, attestation.msg_point, n)
falcon::verify_uncompressed::<512>(attestation.s1, attestation.pk, attestation.msg_point, n)
.expect('Invalid signature');
}
println!("OK");
Expand Down
5 changes: 5 additions & 0 deletions packages/sphincs-plus/Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ cairo_execute = "2.11.4"

[dev-dependencies]
cairo_test = "2.11.4"

[features]
default = []
blake_hash = []
sparse_addr = []
142 changes: 13 additions & 129 deletions packages/sphincs-plus/src/address.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,19 @@
//
// SPDX-License-Identifier: MIT

//! Address structure aligned for use with SHA2/Blake2s hash functions.
//! See https://www.di-mgt.com.au/pqc-09-fors-sig.html for layout details.
// Available address implementations.
pub mod dense;
pub mod sparse;

use crate::word_array::{WordArray, WordArrayTrait};
// Default address packing according to the sha256-128s parameters.
#[cfg(not(feature: "sparse_addr"))]
pub use dense::{Address, AddressTrait};

/// FORS address layout:
/// 0 4 8 12 16 20
/// [0 111] [1111] [1 2 xx] [3 3 xx] [x 4 55] [55]
///
/// WOTS address layout:
/// 0 4 8 12 16 20
/// [0 111] [1111] [1 2 xx] [3 3 xx] [x 6 xx] [x 7]
///
/// Where:
/// 0. Hypertree layer (1 byte)
/// 1. Hypertree address (8 bytes)
/// 2. Address type (1 byte)
/// 3. Keypair hi/lo (2 bytes)
/// 4. Tree height (1 byte)
/// 5. Tree index (4 bytes)
/// 6. Wots chain address (1 byte)
/// 7. Wots hash address (1 byte)
#[derive(Drop, Copy, Default, Debug)]
pub struct Address {
w0: u32, // layer, hypertree address
w1: u32, // hypertree address
w2: u32, // hypertree address, address type
w3: u32, // keypair high/low bytes
w4: u32, // tree height | wots chain address
w5: u32, // tree index | wots hash address (we use lower bytes)
// Cached values
w0_a: u32,
w0_bcd: u32,
w2_a: u32,
w2_b: u32,
w4_b: u32,
w4_cd: u32,
}
// Cairo-friendly address packing.
#[cfg(feature: "sparse_addr")]
pub use sparse::{Address, AddressTrait};

#[derive(Drop, Copy)]
#[derive(Drop)]
pub enum AddressType {
WOTS, // 0
WOTSPK, // 1
Expand All @@ -52,81 +25,6 @@ pub enum AddressType {
FORSPRF // 6
}

#[generate_trait]
pub impl AddressImpl of AddressTrait {
fn from_components(w0: u32, w1: u32, w2: u32, w3: u32, w4: u32, w5: u32) -> Address {
let (w0_a, w0_bcd) = DivRem::div_rem(w0, 0x1000000);
let (w2_a, w2_b) = DivRem::div_rem(w2 / 0x10000, 0x100);
let (w4_b, w4_cd) = DivRem::div_rem(w4 % 0x1000000, 0x10000);
Address { w0, w1, w2, w3, w4, w5, w0_a, w0_bcd, w2_a, w2_b, w4_b, w4_cd }
}

fn set_hypertree_layer(ref self: Address, layer: u8) {
self.w0_a = layer.into() * 0x1000000;
self.w0 = self.w0_a + self.w0_bcd;
}

fn set_hypertree_address(ref self: Address, tree_address: u64) {
let (abc, defgh) = DivRem::div_rem(tree_address, 0x10000000000);
let (defg, h) = DivRem::div_rem(defgh, 0x100);
self.w0_bcd = abc.try_into().unwrap();
self.w0 = self.w0_a + self.w0_bcd;
self.w1 = defg.try_into().unwrap();
self.w2_a = h.try_into().unwrap() * 0x1000000;
// we don't care about the lowest 2 bytes (they are not used and set to zero)
self.w2 = self.w2_a + self.w2_b;
}

fn set_address_type(ref self: Address, address_type: AddressType) {
self.w2_b = address_type.into() * 0x10000;
// we don't care about the lowest 2 bytes (they are not used and set to zero)
self.w2 = self.w2_a + self.w2_b;
}

fn set_keypair(ref self: Address, keypair: u16) {
// we don't care about the lowest 2 bytes (they are not used and set to zero)
self.w3 = keypair.into() * 0x10000;
}

fn set_tree_height(ref self: Address, tree_height: u8) {
self.w4_b = tree_height.into() * 0x10000;
// we don't care about the highest byte (it is not used and set to zero)
self.w4 = self.w4_b + self.w4_cd;
}

fn set_tree_index(ref self: Address, tree_index: u32) {
let (ab, cd) = DivRem::div_rem(tree_index, 0x10000);
self.w4_cd = ab;
// we don't care about the highest byte (it is not used and set to zero)
self.w4 = self.w4_b + self.w4_cd;
// we use lower bytes for compatibility with [WordArray]
self.w5 = cd;
}

fn set_chain_address(ref self: Address, chain_address: u8) {
// In WOTS address other bytes are not used and set to zero.
self.w4_b = chain_address.into() * 0x10000;
// Other bytes are unused
self.w4 = self.w4_b
}

fn set_hash_address(ref self: Address, hash_address: u8) {
// In WOTS address other bytes are not used and set to zero.
// We use lower bytes for compatibility with [WordArray]
self.w5 = hash_address.into();
}

fn to_word_array(self: Address) -> WordArray {
WordArrayTrait::new(array![self.w0, self.w1, self.w2, self.w3, self.w4], self.w5, 2)
}
}

impl AddressTypeDefault of Default<AddressType> {
fn default() -> AddressType {
AddressType::WOTS
}
}

impl AddressTypeToU32 of Into<AddressType, u32> {
fn into(self: AddressType) -> u32 {
match self {
Expand All @@ -141,22 +39,8 @@ impl AddressTypeToU32 of Into<AddressType, u32> {
}
}

#[cfg(test)]
mod tests {
use crate::word_array::hex::words_to_hex;
use super::*;

#[test]
fn test_fors_tree_address() {
let mut address: Address = Default::default();
address.set_hypertree_layer(0);
address.set_hypertree_address(14512697849565227);
address.set_address_type(AddressType::FORSTREE);
address.set_keypair(102);
address.set_tree_height(0);
address.set_tree_index(2765);
let res = words_to_hex(address.to_word_array().span());
let expected = "0000338f38c80e502b03000000660000000000000acd";
assert_eq!(res, expected);
impl AddressTypeDefault of Default<AddressType> {
fn default() -> AddressType {
AddressType::WOTS
}
}
Loading
Loading