diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 696efb3d..cb41d5cc 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -27,6 +27,7 @@ jobs: env: MDBOOK_VERSION: 0.4.43 MDBOOK_KATEX_VERSION: 0.9.2 + MDBOOK_LINKCHECK_VERSION: 0.7.7 steps: - uses: actions/checkout@v4 @@ -42,16 +43,14 @@ jobs: run: | cargo binstall --no-confirm --version ${MDBOOK_VERSION} mdbook cargo binstall --no-confirm --version ${MDBOOK_KATEX_VERSION} mdbook-katex + cargo binstall --no-confirm --version ${MDBOOK_LINKCHECK_VERSION} mdbook-linkcheck - name: Setup Pages id: pages uses: actions/configure-pages@v5 - name: Build with mdBook - run: | - cargo run --bin create_mdbook - cp -r assets book/ - mdbook build + run: mdbook build - name: Upload artifact uses: actions/upload-pages-artifact@v3 @@ -64,7 +63,7 @@ jobs: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest - needs: build + needs: [build] steps: - name: Deploy to GitHub Pages id: deployment diff --git a/.github/workflows/mdbook-build.yaml b/.github/workflows/mdbook-build.yaml new file mode 100644 index 00000000..0ea1d1c6 --- /dev/null +++ b/.github/workflows/mdbook-build.yaml @@ -0,0 +1,32 @@ +name: MDBook Build + +on: + pull_request: + branches: ["main"] + +jobs: + test: + runs-on: ubuntu-latest + env: + MDBOOK_VERSION: 0.4.43 + MDBOOK_KATEX_VERSION: 0.9.2 + MDBOOK_LINKCHECK_VERSION: 0.7.7 + steps: + - uses: actions/checkout@v4 + + - name: Install cargo-binstall + uses: cargo-bins/cargo-binstall@main + + - name: Install rust + run: | + curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh + rustup update + + - name: Install mdbook and plugins + run: | + cargo binstall --no-confirm --version ${MDBOOK_VERSION} mdbook + cargo binstall --no-confirm --version ${MDBOOK_KATEX_VERSION} mdbook-katex + cargo binstall --no-confirm --version ${MDBOOK_LINKCHECK_VERSION} mdbook-linkcheck + + - name: Build mdBook + run: mdbook build \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 0defb709..fda332c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,12 +9,12 @@ version ="0.1.1" exclude =["CHANGELOG.md", "assets/"] [dependencies] -rand ="0.8" -itertools ="0.14" -hex ="0.4" -crypto-bigint ="0.6.0-rc.6" -regex ="1.11.1" -num-traits ="0.2.19" +rand ="0.8" +itertools ="0.14" +hex ="0.4" +crypto-bigint="0.6.0-rc.6" +regex ="1.11.1" +num-traits ="0.2.19" [dev-dependencies] rstest ="0.24" diff --git a/README.md b/README.md index 803587fd..20ea5ed8 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Ronkathon is a collection of cryptographic primitives implemented in Rust. It is - **Fundamental Algebraic Structures** - [Group](src/algebra/group/README.md) - [Fields and Their Extensions](src/algebra/field/README.md) - - [Binary Fields](src/field/binary_towers/README.md) + - [Binary Fields](src/algebra/field/binary_towers/README.md) - [Curves and Their Pairings](src/curve/README.md) - [Polynomials](src/polynomial/mod.rs) - [KZG Commitments](src/kzg/README.md) @@ -82,13 +82,13 @@ In particular, the `math/field.sage` computes roots of unity in the `PlutoField` ## Building mdBook To locally build/serve the [mdBook](https://github.com/rust-lang/mdBook) site, install mdBook and [mdbook-katex](https://github.com/lzanini/mdbook-katex): -``` +```ignore cargo install mdbook cargo install mdbook-katex ``` To build, run: -``` +```ignore cargo run --bin create_mdbook cp -r assets book/ mdbook build @@ -98,7 +98,7 @@ If you want to serve locally, run `mdbook serve`. ## License -Licensed under the Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +Licensed under the Apache License, Version 2.0 ([LICENSE-APACHE](./LICENSE) or http://www.apache.org/licenses/LICENSE-2.0) ## Contributing diff --git a/book.toml b/book.toml index a64aa4f3..9996b3c5 100644 --- a/book.toml +++ b/book.toml @@ -1,20 +1,41 @@ [book] -authors = ["Contributors to Ronkathon"] -language = "en" -multilingual = false -src = "book" -title = "Ronkathon: Cryptography Educational Foundations" -description = "Cryptography Educational Foundations" +authors =["Contributors to Ronkathon"] +language ="en" +multilingual=false +src ="." +title ="Ronkathon: Cryptography Educational Foundations" +description ="Cryptography Educational Foundations" [build] -build-dir = "docs" -use-default-preprocessors = true -create-missing = true +build-dir ="docs" +use-default-preprocessors=true +create-missing =true [preprocessor.katex] -after = ["links"] +after=["links"] + +[preprocessor.linkcheck] +follow-web-links =true +traverse-parent-directories=true + +# TODO (autoparallel): there's a bunch to fix with this +# [preprocessor.keeper] +# command ="mdbook-keeper" +# manifest_dir="." +# externs =["ronkathon", "rand"] [output.html] -default-theme = "dark" -preferred-dark-theme = "ayu" -git-repository-url = "https://github.com/pluto/ronkathon" +default-theme ="dark" +preferred-dark-theme="ayu" +git-repository-url ="https://github.com/pluto/ronkathon" + +[output.html.playground] +editable=true +runnable=true + +[rust] +edition="2021" + +[[output.html.playground.pre-installed-crates]] +name ="rand" +version="0.8" diff --git a/src/algebra/field/README.md b/src/algebra/field/README.md index 3e7da65b..9793e40c 100644 --- a/src/algebra/field/README.md +++ b/src/algebra/field/README.md @@ -46,7 +46,7 @@ The structs that implement these traits are ### `PrimeField` The `PrimeField` struct is a wrapper around a `usize` by: -```rust +```rust,ignore pub struct PrimeField { value: usize, } @@ -62,7 +62,7 @@ All of the relevant arithmetic operations for `PrimeField

` are implemented in ### `GaloisField` The `GaloisField` struct is a wrapper around a `PrimeField

` by: -```rust +```rust,ignore use ronkathon::algebra::field::prime::PrimeField; pub struct GaloisField { value: [PrimeField

; N], diff --git a/src/algebra/group/README.md b/src/algebra/group/README.md index 91e0b056..94c4b730 100644 --- a/src/algebra/group/README.md +++ b/src/algebra/group/README.md @@ -19,7 +19,7 @@ The structs that implement these traits are ### `MultiplicativePrimeGroup` The `MultiplicativePrimeGroup` struct is a wrapper around a `usize` that defines $(Z/nZ)^{*}$ for a prime power $n=p^k$ with binary operation as $\times$: -```rust +```rust,ignore pub struct MultiplicativePrimeGroup(usize); ``` diff --git a/src/bin/create_mdbook.rs b/src/bin/create_mdbook.rs deleted file mode 100644 index 09128e6c..00000000 --- a/src/bin/create_mdbook.rs +++ /dev/null @@ -1,78 +0,0 @@ -/// 1. Read SUMMARY.md and copy `README.md` files given in it to `book` directory. -/// 2. Change the links to other `README.md` files to `index.md`, so that link points to -/// correct file in the mdbook. -/// 3. Change links that point to a '.rs' file to their github repo link. -use std::{ - fs::{self, File}, - io::{self, BufRead, BufReader, Write}, - path::{Path, PathBuf}, -}; - -use regex::Regex; - -const DEST: &str = "book"; -const REPO_LINK: &str = "https://github.com/pluto/ronkathon/blob/main/"; -const SHOW_CHANGES: bool = false; - -fn main() -> io::Result<()> { - let dest_path = Path::new(DEST); - if !dest_path.exists() { - fs::create_dir(dest_path)?; - } - - let mut readmes = Vec::::new(); - let f = File::open("SUMMARY.md")?; - let reader = BufReader::new(f); - - let re = Regex::new(r"\[.*\]\((.*)\)").unwrap(); - - for line in reader.lines() { - for (_, [link]) in re.captures_iter(&line?).map(|c| c.extract()) { - if !link.is_empty() { - readmes.push(PathBuf::from(link)); - } - } - } - - let readme_re = Regex::new(r"README.md").unwrap(); - let rs_links = Regex::new(r"(?\[.*\])\([/]?(?.*\.rs)\)").unwrap(); - - for src in &readmes { - println!("Working on: {}", src.display()); - let mut src_parent = src.parent().unwrap().to_str().unwrap().to_owned(); - if !src_parent.is_empty() { - src_parent.push('/'); - } - - let dest = Path::new(DEST).join(src); - let dest_folder = dest.parent().unwrap(); - if !dest_folder.exists() { - fs::create_dir_all(dest_folder)?; - } - let src_file = File::open(src)?; - let reader = BufReader::new(src_file); - - let mut dest_file = File::create(&dest)?; - - for line in reader.lines() { - let before = line.unwrap(); - let after1 = readme_re.replace_all(&before, "index.md"); - if before != after1 && SHOW_CHANGES { - println!("1. {before} -> {after1}"); - } - let after2 = rs_links.replace_all(&after1, format!("$t({}{}$l)", REPO_LINK, src_parent)); - if after1 != after2 && SHOW_CHANGES { - println!("2. {after1} -> {after2}"); - } - dest_file.write_all(after2.as_bytes())?; - dest_file.write_all(b"\n")?; - } - } - - println!("Copying SUMMARY.md to {DEST}/SUMMARY.md"); - fs::copy("SUMMARY.md", "book/SUMMARY.md")?; - - println!("Done!"); - - Ok(()) -} diff --git a/src/compiler/README.md b/src/compiler/README.md index 4c0de6ef..890a0be7 100644 --- a/src/compiler/README.md +++ b/src/compiler/README.md @@ -24,7 +24,7 @@ Outputs parsed output in form of `WireCoeffs` values and coefficients. - `coefficients`: coefficient corresponding to each variable. -```rust +```rust,ignore use std::collections::HashMap; /// Values of wires with coefficients of each wire name #[derive(Debug, PartialEq)] @@ -68,7 +68,7 @@ r2[right input]-->b b-->o2[output=l+r] ``` -```rust +```rust,ignore use ronkathon::algebra::field::prime::PlutoScalarField; /// Fan-in 2 Gate representing a constraint in the computation. /// Each constraint satisfies PLONK's arithmetic equation: `a(X)QL(X) + b(X)QR(X) + a(X)b(X)QM(X) + @@ -107,7 +107,7 @@ Converts `WireValues` to required polynomials in PLONK, i.e. To get selector polynomials from constraints, each constraint is parsed into fan-in 2 arithmetic gates as explained above and wire values are assigned to respective wires in lagrange form. -```rust +```rust,ignore /// `CommonPreprocessedInput` represents circuit related input which is apriori known to `Prover` /// and `Verifier` involved in the process. use ronkathon::{ @@ -154,8 +154,8 @@ permutation helper creates $\sigma_i$ polynomials for $i = \{1,2,3\}$. - for example: x's usage gets shifted 1 to right, new one becomes: `{'x': [(3, RIGHT), (1, LEFT), (2, LEFT), (2, RIGHT)]}`. - This ensures that variables `x` is copied from $x_i$ to $x_{i+1}$ -```rust - use ronkathon::compiler::parser::WireCoeffs; +```rust,ignore +use ronkathon::compiler::parser::WireCoeffs; /// `Program` represents constraints used while defining the arithmetic on the inputs /// and group order of primitive roots of unity in the field. diff --git a/src/encryption/symmetric/aes/README.md b/src/encryption/symmetric/aes/README.md index ea6b3468..2833f5de 100644 --- a/src/encryption/symmetric/aes/README.md +++ b/src/encryption/symmetric/aes/README.md @@ -45,7 +45,7 @@ To generate more round keys out of the original key, we do a series of word rota For round **i**, if i is a multiple of the length of the key (in words): -```rust +```rust,ignore Self::rotate_word(&mut last); word = (u32::from_le_bytes(Self::sub_word(last)) ^ u32::from_le_bytes(ROUND_CONSTANTS[(i / key_len) - 1])) @@ -54,13 +54,13 @@ For round **i**, if i is a multiple of the length of the key (in words): if i + 4 is a multiple of 8: -```rust +```rust,ignore word = Self::sub_word(last) ``` The final step is always to XOR previous round's round key with the *(i - key_len)*-th round key: -```rust +```rust,ignore let round_key = expanded_key[i - key_len] .iter() .zip(last.iter()) @@ -132,8 +132,8 @@ In production-level AES code, fast AES software uses special techniques called t ## References -- [FIPS197](fips197) -- [Serious Cryptography - A Practical Introduction to Modern Cryptography](seriouscrypto) +- [FIPS197][fips197] +- [Serious Cryptography - A Practical Introduction to Modern Cryptography][seriouscrypto] [aes]: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard [des]: ../des/README.md diff --git a/src/encryption/symmetric/chacha/README.md b/src/encryption/symmetric/chacha/README.md index 53abac2c..d25c1456 100644 --- a/src/encryption/symmetric/chacha/README.md +++ b/src/encryption/symmetric/chacha/README.md @@ -21,7 +21,7 @@ Then, chacha stream cipher's internal state is defined using $F$ with a 256-bit Let's define what happens inside $F$, it runs a quarter round that takes as input 4 4-byte input and apply constant time ARX operations: -``` +```ignore a += b; d ^= a; d <<<= 16; c += d; b ^= c; b <<<= 12; a += b; d ^= a; d <<<= 8; @@ -42,7 +42,7 @@ During initial round, **counters** are initialised to 0, and for next rounds, in [uct]: [ietf]: -[xchacha]: +[xchacha]: [salsa]: [chacha]: -[chacha-family]: \ No newline at end of file +[chacha-family]: diff --git a/src/encryption/symmetric/modes/gcm.rs b/src/encryption/symmetric/modes/gcm.rs index 0ae4f581..bc966d60 100644 --- a/src/encryption/symmetric/modes/gcm.rs +++ b/src/encryption/symmetric/modes/gcm.rs @@ -1,5 +1,5 @@ //! Implementation of GCM cipher mode of operation based on NIST GCM specification. -//! [The Galois/Counter Mode of Operation (GCM)](http://www.csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf) +//! [The Galois/Counter Mode of Operation (GCM)](https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf) //! //! GCM has two parts GCTR (used of encryption/decryption) and GHASH (used for authentication). //! diff --git a/src/hashes/ghash.rs b/src/hashes/ghash.rs index 625ac28f..b27bf854 100644 --- a/src/hashes/ghash.rs +++ b/src/hashes/ghash.rs @@ -1,7 +1,7 @@ //! Implementation of [`GHASH`] algorithm which is used in AES-GCM to compute the authentication //! tag. //! Based on GCM specification given by NIST: -//! [The Galois/Counter Mode of Operation (GCM)](http://www.csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf) +//! [The Galois/Counter Mode of Operation (GCM)](https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf) //! //! ASCII diagram of GHASH, courtesy of @0xJepsen: //! X1 X2 ... XM diff --git a/src/hashes/poseidon/README.md b/src/hashes/poseidon/README.md index 931973e0..8895e756 100644 --- a/src/hashes/poseidon/README.md +++ b/src/hashes/poseidon/README.md @@ -91,13 +91,14 @@ sage poseidon_constants.sage 1 0 7 16 3 10 0x65 Here's an example to use [Poseidon](./mod.rs) struct for hashing input of length `WIDTH`. Note that input is padded with extra zeroes if length is not equal to width. ```rust -use ronkathon::field::prime::PlutoBaseField; // can be any field that implements FiniteField trait +use ronkathon::{hashes::poseidon::Poseidon, field::prime::PlutoBaseField}; // can be any field that implements FiniteField trait const WIDTH: usize = 10; const ALPHA: usize = 5; const NUM_P: usize = 16; const NUM_F: usize = 8; +# pub fn main() { // load round constants and mds matrix let (rc, mds) = load_constants::(); @@ -111,14 +112,21 @@ let input = std::iter::repeat(PlutoBaseField::ZERO).take(WIDTH).collect(); let res = poseidon.hash(input); println!("{:?}", res); +# } ``` Another example using Sponge API for arbitrary length element hashing. Simplex sponge supports arbitrary length absorption with arbitrary length squeeze. ```rust -use rand::rng; -use ronathon::field::prime::PlutoBaseField; +use rand::Rng; +use ronkathon::{field::prime::PlutoBaseField, hashes::poseidon::sponge::PoseidonSponge}; +const WIDTH: usize = 10; +const ALPHA: usize = 5; +const NUM_P: usize = 16; +const NUM_F: usize = 8; + +# pub fn main() { let size = rng.gen::(); // create any state @@ -126,13 +134,14 @@ let input = std::iter::repeat(PlutoBaseField::ONE).take(size).collect(); let (rc, mds) = load_constants::(); -let mut pluto_poseidon_sponge = PoseidonSponge::new(WIDTH, ALPHA, NUM_P, NUM_F, rate, rc, mds) +let mut pluto_poseidon_sponge = PoseidonSponge::new(WIDTH, ALPHA, NUM_P, NUM_F, rate, rc, mds); let absorb_res = pluto_poseidon_sponge.absorb(&input); assert!(absorb_res.is_ok()); let pluto_result = pluto_poseidon_sponge.squeeze(squeeze_size); assert!(pluto_result.is_ok()); +# } ``` More info and examples can be found in [tests](./tests/mod.rs).