├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── benchmark.rs └── src ├── algebra ├── matrix.rs ├── mod.rs ├── scalar.rs └── vector.rs ├── l2 └── mod.rs ├── lib.rs └── lll └── mod.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lll-rs" 3 | version = "0.3.0" 4 | authors = ["Rémi Géraud-Stewart ", "Stanislas Plessia ", "Edmond de Roffignac "] 5 | edition = "2018" 6 | description = "Implementation of the LLL algorithm for lattice reduction and it's improved version L²" 7 | license = "MIT" 8 | readme = "README.md" 9 | keywords = ["lll", "lattice", "algebra"] 10 | categories = ["cryptography", "science"] 11 | repository = "https://github.com/rust-crypto-labs/lll-rs" 12 | 13 | [dependencies] 14 | rug = "1.*" 15 | 16 | [dev-dependencies] 17 | criterion = "0.3" 18 | 19 | [profile.release] 20 | opt-level = 3 21 | debug = false 22 | lto = true 23 | 24 | [profile.bench] 25 | opt-level = 3 26 | debug = false 27 | lto = true 28 | 29 | [[bench]] 30 | name = "benchmark" 31 | harness = false 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Plessia Stanislas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lll-rs 2 | 3 | `lll-rs` is an implementation of the Lenstra–Lenstra–Lovász lattice basis reduction algorithm (LLL [[LLL82](#LLL82)], [1a], [1b]) in Rust. 4 | 5 | ## Supported algorithms 6 | 7 | - LLL reduction [1a] 8 | - L² reduction [2] 9 | - Standard Gram-Schmidt orthogonalisation 10 | 11 | The library comes with a set of simple helpers to create vectors and matrices, with the following entries: 12 | 13 | - Integers (`BigVector`, relying on `rug::Integer`) 14 | - Rationals (`RationalVector`, relying on `rug::Rational`) 15 | - Small rationals (`VectorF`, relying on `f64`) 16 | 17 | `lll-rs` is far from feature-complete and should be considered experimental. Users willing to use a stable and battle-tested library should 18 | consider `fplll` instead [fplll]. 19 | 20 | ## Lattice reduction 21 | 22 | A lattice Λ is a dicrete subgroup of some vector space E. A typical example (see e.g. [3]) is E = ℝⁿ and 23 | 24 | `X ∊ Λ <=> X = l_1 * b_1 + ... + l_n * b_n with (l_i) in ℤ and (b_i) in ℝ` 25 | 26 | Lattices are much studied mathematical structures on which we can formulate some useful problems [4]. Some of 27 | these problems are simpler to solve when a "good basis" is known for the lattice. Conversely it is 28 | difficult to solve them when only a "bad basis" is known. 29 | 30 | Simply put, the LLL algorithm provides such a "good basis"; it roughly does so by performing a (variant of) rounded Gram-Schimdt orthogonalization on the "bad basis". 31 | Remarkably, this algorithm runs in polynomial time which makes it possible to solve several lattice problems efficiently. 32 | 33 | Applications of LLL include: 34 | 35 | - Cryptanalysis of lattice-based cryptosystems (e.g. NTRU) 36 | - Cryptanalysis of pseudo-random number generators (e.g. LCG and truncated LCG) 37 | - Cryptanalysis of RSA (e.g. Coppersmith's attack [5]) 38 | - Cryptanalysis of knapsack-based cryptosystems 39 | - Finding mathematical counterexamples (e.g. Merten's conjecture) 40 | - Finding roots of polynomials with integer coefficients 41 | - Finding integer relations between constants 42 | - Decoding of error correcting codes 43 | 44 | ## Example 45 | 46 | ```rust 47 | // Init the matrix with Integer 48 | let mut basis: Matrix = Matrix::init(3, 4); 49 | 50 | // Populate the matix 51 | basis[0] = BigVector::from_vector(vec![ 52 | Integer::from(1) << 100000, 53 | Integer::from(0), 54 | Integer::from(0), 55 | Integer::from(1345), 56 | ]); 57 | basis[1] = BigVector::from_vector(vec![ 58 | Integer::from(0), 59 | Integer::from(1), 60 | Integer::from(0), 61 | Integer::from(35), 62 | ]); 63 | basis[2] = BigVector::from_vector(vec![ 64 | Integer::from(0), 65 | Integer::from(0), 66 | Integer::from(1), 67 | Integer::from(154), 68 | ]); 69 | 70 | // Perfom the LLL basis reduction 71 | biglll::lattice_reduce(&mut basis); 72 | 73 | // OR 74 | // Perfom the L2 basis reduction 75 | // Specify the delta and eta coefficient for the reduction 76 | bigl2::lattice_reduce(&mut basis, 0.5005, 0.999); 77 | ``` 78 | 79 | ## References and documentation 80 | 81 | [LLL82] A. K. Lenstra, H. W. Lenstra, Jr. and L. Lovasz. Factoring polynomials with rational coefficients. Math. Ann., 261: 515–534 (1982) 82 | 83 | - https://openaccess.leidenuniv.nl/bitstream/handle/1887/3810/346_050.pdf 84 | - https://en.wikipedia.org/wiki/Lenstra–Lenstra–Lovász_lattice_basis_reduction_algorithm 85 | - https://perso.ens-lyon.fr/damien.stehle/downloads/LLL25.pdf 86 | - https://en.wikipedia.org/wiki/Lattice_(group) 87 | - https://en.wikipedia.org/wiki/Lattice_problem 88 | - https://en.wikipedia.org/wiki/Coppersmith%27s_attack 89 | 90 | [1a]: https://openaccess.leidenuniv.nl/bitstream/handle/1887/3810/346_050.pdf 91 | [1b]: https://en.wikipedia.org/wikiLenstra–Lenstra–Lovász_lattice_basis_reduction_algorithm 92 | [2]: https://perso.ens-lyon.fr/damien.stehle/downloads/LLL25.pdf 93 | [3]: https://en.wikipedia.org/wiki/Lattice_(group) 94 | [4]: https://en.wikipedia.org/wiki/Lattice_problem 95 | [5]: https://en.wikipedia.org/wiki/Coppersmith%27s_attack 96 | [fplll]: https://github.com/fplll/fplll -------------------------------------------------------------------------------- /benches/benchmark.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | extern crate lll_rs; 4 | extern crate rug; 5 | 6 | mod benchmarks { 7 | use criterion::Criterion; 8 | 9 | use lll_rs::{l2, lll, Matrix}; 10 | 11 | pub fn bench_big_int_reduction_lll(c: &mut Criterion) { 12 | type I = rug::Integer; 13 | // "Bad" lattice basis 14 | let mut basis: Matrix = Matrix::from_matrix(vec![ 15 | vec![ 16 | I::from(1) << 10000, 17 | I::from(0), 18 | I::from(0), 19 | I::from(1345) << 789, 20 | ], 21 | vec![ 22 | I::from(0), 23 | I::from(1) << 500, 24 | I::from(0), 25 | I::from(35) << 3505, 26 | ], 27 | vec![ 28 | I::from(0), 29 | I::from(0), 30 | I::from(1) << 1000, 31 | I::from(154) << 5000, 32 | ], 33 | ]); 34 | 35 | c.bench_function("lattice_reduce (biglll)", move |b| { 36 | b.iter(|| lll::lll_bignum(&mut basis)) 37 | }); 38 | } 39 | 40 | pub fn bench_big_int_reduction_l2(c: &mut Criterion) { 41 | type I = rug::Integer; 42 | // "Bad" lattice basis 43 | let mut basis: Matrix = Matrix::from_matrix(vec![ 44 | vec![ 45 | I::from(1) << 10000, 46 | I::from(0), 47 | I::from(0), 48 | I::from(1345) << 789, 49 | ], 50 | vec![ 51 | I::from(0), 52 | I::from(1) << 500, 53 | I::from(0), 54 | I::from(35) << 3505, 55 | ], 56 | vec![ 57 | I::from(0), 58 | I::from(0), 59 | I::from(1) << 1000, 60 | I::from(154) << 5000, 61 | ], 62 | ]); 63 | 64 | c.bench_function("lattice_reduce (bigl2)", move |b| { 65 | b.iter(|| l2::lll_bignum(&mut basis, 0.501, 0.998)) 66 | }); 67 | } 68 | } 69 | 70 | criterion_group!(big_reduce_lll, benchmarks::bench_big_int_reduction_lll); 71 | criterion_group!(big_reduce_l2, benchmarks::bench_big_int_reduction_l2); 72 | criterion_main!(big_reduce_lll, big_reduce_l2); 73 | -------------------------------------------------------------------------------- /src/algebra/matrix.rs: -------------------------------------------------------------------------------- 1 | //! Basic matrix structure for LLL 2 | 3 | use super::{Coefficient, Vector}; 4 | 5 | use std::{ 6 | fmt, 7 | ops::{Index, IndexMut}, 8 | }; 9 | 10 | #[derive(PartialEq)] 11 | /// A `Matrix` is a collection of `Vector`s 12 | pub struct Matrix { 13 | /// Internal representation as a list of elements of type `T` 14 | columns: Vec>, 15 | 16 | /// Dimensions of the matrix 17 | dimensions: (usize, usize), 18 | } 19 | 20 | impl Matrix { 21 | /// Initialise an empty `Matrix` 22 | /// - `col_num`: number of columns 23 | /// - `col_dim`: number of rows 24 | pub fn init(col_num: usize, col_dim: usize) -> Self { 25 | Self { 26 | columns: vec![Vector::::init(col_dim); col_num], 27 | dimensions: (col_num, col_dim), 28 | } 29 | } 30 | 31 | pub fn from_columns(columns: Vec>) -> Self { 32 | let dimensions = if let Some(col) = columns.first() { 33 | (columns.len(), col.dimension()) 34 | } else { 35 | (0, 0) 36 | }; 37 | Self { 38 | columns, 39 | dimensions, 40 | } 41 | } 42 | 43 | pub fn from_matrix(matrix: Vec>) -> Self { 44 | Self::from_columns( 45 | matrix 46 | .iter() 47 | .map(|column| Vector::::from_vector(column.to_vec())) 48 | .collect(), 49 | ) 50 | } 51 | 52 | /// Return the matrix dimensions 53 | pub fn dimensions(&self) -> (usize, usize) { 54 | self.dimensions 55 | } 56 | 57 | /// Swap two columns of the matrix 58 | pub fn swap(&mut self, i: usize, j: usize) { 59 | self.columns.swap(i, j); 60 | } 61 | 62 | /// Insert the i-th column before the j-th one 63 | pub fn insert(&mut self, i: usize, j: usize) { 64 | let v = self.columns.remove(i); 65 | self.columns.insert(j, v) 66 | } 67 | } 68 | 69 | /// Direct access to a column 70 | impl Index for Matrix { 71 | type Output = Vector; 72 | 73 | fn index(&self, index: usize) -> &Self::Output { 74 | &self.columns[index] 75 | } 76 | } 77 | 78 | /// Direct access to a column (mutable) 79 | impl IndexMut for Matrix { 80 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 81 | &mut self.columns[index] 82 | } 83 | } 84 | 85 | impl fmt::Debug for Matrix { 86 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 87 | writeln!(f, "{:?}", self.columns) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/algebra/mod.rs: -------------------------------------------------------------------------------- 1 | mod matrix; 2 | mod scalar; 3 | mod vector; 4 | 5 | pub use matrix::Matrix; 6 | pub use scalar::{BigNum, Coefficient, Float, FromExt, Scalar}; 7 | pub use vector::Vector; 8 | -------------------------------------------------------------------------------- /src/algebra/scalar.rs: -------------------------------------------------------------------------------- 1 | use rug::{Integer, Rational}; 2 | use std::{ 3 | cmp::PartialOrd, 4 | fmt::Debug, 5 | iter::Sum, 6 | ops::{Add, Div, Mul, Sub, SubAssign}, 7 | }; 8 | 9 | pub trait Coefficient: 10 | From 11 | + PartialEq 12 | + PartialOrd 13 | + Clone 14 | + Debug 15 | + Default 16 | + for<'a> Add<&'a Self, Output = Self> 17 | + for<'a> Sub<&'a Self, Output = Self> 18 | + for<'a> SubAssign<&'a Self> 19 | + for<'a> Mul<&'a Self, Output = Self> 20 | + Sum 21 | { 22 | } 23 | 24 | impl Coefficient for T where 25 | T: From 26 | + PartialEq 27 | + PartialOrd 28 | + Clone 29 | + Debug 30 | + Default 31 | + for<'a> Add<&'a Self, Output = Self> 32 | + for<'a> Sub<&'a Self, Output = Self> 33 | + for<'a> SubAssign<&'a Self> 34 | + for<'a> Mul<&'a Self, Output = Self> 35 | + Sum 36 | { 37 | } 38 | 39 | pub trait FromExt { 40 | fn from_ext(_: T) -> Self; 41 | } 42 | 43 | macro_rules! impl_from_ext { 44 | ($from_type:ty, $to_type:ty, $code:expr) => { 45 | impl<'a> FromExt<$from_type> for $to_type { 46 | fn from_ext(f: $from_type) -> Self { 47 | $code(f) 48 | } 49 | } 50 | }; 51 | } 52 | 53 | pub trait Scalar { 54 | type Integer: Coefficient; 55 | type Fraction: Coefficient 56 | + PartialOrd 57 | + FromExt 58 | + FromExt<(Self::Integer, Self::Integer)> 59 | + FromExt<(i32, i32)> 60 | + for<'a> FromExt<&'a Self::Integer> 61 | + for<'a> Div<&'a Self::Fraction, Output = Self::Fraction>; 62 | 63 | fn round(n: &Self::Fraction) -> Self::Integer; 64 | fn round_div(n: Self::Integer, d: Self::Integer) -> Self::Integer; 65 | fn abs(f: Self::Fraction) -> Self::Fraction; 66 | } 67 | 68 | impl_from_ext!(&f64, f64, |f: &f64| *f); 69 | impl_from_ext!((f64, f64), f64, |(n, d)| n / d); 70 | impl_from_ext!(f64, f64, |f| f); 71 | impl_from_ext!((i32, i32), f64, |(n, d)| f64::from(n) / f64::from(d)); 72 | 73 | pub struct Float; 74 | 75 | impl Scalar for Float { 76 | type Integer = f64; 77 | type Fraction = f64; 78 | 79 | fn round(f: &Self::Fraction) -> Self::Integer { 80 | let (int, fract) = (f.trunc(), f.fract()); 81 | if fract.abs() > 0.5 { 82 | f.signum() * (int.abs() + 1.) 83 | } else { 84 | int 85 | } 86 | } 87 | 88 | fn round_div(n: Self::Integer, d: Self::Integer) -> Self::Integer { 89 | (n / d).round() 90 | } 91 | 92 | fn abs(f: Self::Fraction) -> Self::Fraction { 93 | f.abs() 94 | } 95 | } 96 | 97 | impl_from_ext!(&Integer, Rational, |f: &Integer| Rational::from(f)); 98 | impl_from_ext!((Integer, Integer), Rational, |(n, d)| Rational::from(( 99 | n, d 100 | ))); 101 | impl_from_ext!(f64, Rational, |f: f64| Rational::from_f64(f).unwrap()); 102 | impl_from_ext!((i32, i32), Rational, |(n, d)| Rational::from((n, d))); 103 | 104 | pub struct BigNum; 105 | 106 | impl Scalar for BigNum { 107 | type Integer = rug::Integer; 108 | type Fraction = rug::Rational; 109 | 110 | fn round(f: &Self::Fraction) -> Self::Integer { 111 | let (fract, trunc) = f.clone().fract_trunc(Integer::new()); 112 | if fract.abs() > (1_u16, 2_u16) { 113 | f.clone().signum().numer() * (trunc.abs() + Integer::from(1)) 114 | } else { 115 | trunc 116 | } 117 | } 118 | 119 | fn round_div(mut n: Self::Integer, mut d: Self::Integer) -> Self::Integer { 120 | n.div_rem_round_mut(&mut d); 121 | n 122 | } 123 | 124 | fn abs(f: Self::Fraction) -> Self::Fraction { 125 | f.abs() 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/algebra/vector.rs: -------------------------------------------------------------------------------- 1 | //! Basic vector structures for LLL 2 | use super::Coefficient; 3 | 4 | use std::{ 5 | fmt, 6 | ops::{Index, IndexMut}, 7 | }; 8 | 9 | /// Implementation of a vector without generic coefficients 10 | #[derive(Clone, PartialEq)] 11 | pub struct Vector { 12 | /// Internal representation as a list of coefficients 13 | coefficients: Vec, 14 | } 15 | 16 | impl Vector { 17 | pub fn basis_vector(dimension: usize, position: usize) -> Self { 18 | assert!(position < dimension); 19 | 20 | let coefficients = (0..dimension) 21 | .map(|i| { 22 | if i == position { 23 | T::from(1) 24 | } else { 25 | T::from(0) 26 | } 27 | }) 28 | .collect(); 29 | 30 | Self { coefficients } 31 | } 32 | 33 | pub fn init(dimension: usize) -> Self { 34 | Self { 35 | coefficients: vec![Default::default(); dimension], 36 | } 37 | } 38 | 39 | pub fn dimension(&self) -> usize { 40 | self.coefficients.len() 41 | } 42 | 43 | pub fn add(&self, other: &Self) -> Self { 44 | let n = self.dimension(); 45 | 46 | assert_eq!(n, other.dimension()); 47 | 48 | Self::from_vector( 49 | (0..n) 50 | .map(|i| self.coefficients[i].clone() + &other.coefficients[i]) 51 | .collect(), 52 | ) 53 | } 54 | 55 | pub fn sub(&self, other: &Self) -> Self { 56 | let n = self.dimension(); 57 | 58 | assert_eq!(n, other.dimension()); 59 | 60 | Self::from_vector( 61 | (0..n) 62 | .map(|i| self.coefficients[i].clone() - &other.coefficients[i]) 63 | .collect(), 64 | ) 65 | } 66 | 67 | /// Create an instance from a `Vec` 68 | pub fn from_vector(coefficients: Vec) -> Self { 69 | Self { coefficients } 70 | } 71 | 72 | /// Multiplication by a scalar 73 | pub fn mulf(&self, other: T) -> Self { 74 | let n = self.dimension(); 75 | 76 | Self::from_vector( 77 | (0..n) 78 | .map(|i| self.coefficients[i].clone() * &other) 79 | .collect(), 80 | ) 81 | } 82 | pub fn zero(dimension: usize) -> Self { 83 | Self { 84 | coefficients: vec![Default::default(); dimension], 85 | } 86 | } 87 | 88 | pub fn is_zero(&self) -> bool { 89 | self == &Vector::zero(self.dimension()) 90 | } 91 | 92 | pub fn dot(&self, other: &Self) -> T { 93 | self.coefficients 94 | .iter() 95 | .zip(&other.coefficients) 96 | .map(|(coeff_r, coeff_l)| coeff_r.clone() * coeff_l) 97 | .sum() 98 | } 99 | } 100 | 101 | impl Index for Vector { 102 | type Output = T; 103 | 104 | fn index(&self, index: usize) -> &T { 105 | &self.coefficients[index] 106 | } 107 | } 108 | 109 | impl IndexMut for Vector { 110 | fn index_mut(&mut self, index: usize) -> &mut T { 111 | &mut self.coefficients[index] 112 | } 113 | } 114 | 115 | impl fmt::Debug for Vector { 116 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 117 | writeln!(f, "{:?}", self.coefficients) 118 | } 119 | } 120 | /* 121 | pub type VectorF = Vector; 122 | pub type BigVector = Vector;*/ 123 | -------------------------------------------------------------------------------- /src/l2/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::algebra::{BigNum, Float, FromExt, Matrix, Scalar, Vector}; 2 | 3 | /// Lattice reduction (L² algorithm) 4 | /// 5 | /// This implementation uses generic Scalar types for the underlying arithmetic operations. 6 | /// 7 | /// Arguments: 8 | /// * basis: A generating matrix for the lattice 9 | /// * eta: eta factor of the basis reduction 10 | /// * delta: delta factor of the basis reduction 11 | /// 12 | /// The basis is reduced in-place. 13 | /// 14 | /// # Panics 15 | /// if delta <= 1/4 or delta >= 1 16 | /// if eta <= 1/2 or eta > sqrt(delta) 17 | fn lattice_reduce(basis: &mut Matrix, eta: f64, delta: f64) { 18 | assert!(0.25 < delta && delta < 1.); 19 | assert!(0.5 < eta && eta * eta < delta); 20 | 21 | // Variables 22 | let (d, _) = basis.dimensions(); 23 | let mut gram: Matrix = Matrix::init(d, d); // Gram matrix (upper triangular) 24 | let mut r: Matrix = Matrix::init(d, d); // r_ij matrix 25 | let mut mu: Matrix = Matrix::init(d, d); // Gram coefficient matrix 26 | let mut s: Vector = Vector::init(d); 27 | let mut m = Vector::init(d); 28 | 29 | let zero = S::Fraction::from(0); 30 | let mut num_zeros = 0; 31 | 32 | // Computing Gram matrix 33 | for i in 0..d { 34 | for j in 0..=i { 35 | gram[i][j] = basis[i].dot(&basis[j]); 36 | } 37 | } 38 | 39 | let eta_minus = S::Fraction::from_ext((eta + 0.5) / 2.); 40 | let delta_plus = S::Fraction::from_ext(0.99); //(delta + 1.) / 2.); 41 | 42 | r[0][0] = S::Fraction::from_ext(&gram[0][0]); 43 | 44 | let mut kappa = 1; 45 | 46 | while kappa < (d - num_zeros) { 47 | size_reduce::(basis, &mut gram, &mut mu, &mut r, &mut m, kappa, &eta_minus); 48 | 49 | s[0] = S::Fraction::from_ext((gram[kappa][kappa].clone(), S::Integer::from(1))); 50 | for i in 0..kappa { 51 | s[i + 1] = s[i].clone() - &(mu[kappa][i].clone() * &r[kappa][i]); 52 | } 53 | 54 | let delta_criterion = |k| delta_plus.clone() * &r[k - 1][k - 1]; 55 | 56 | if delta_criterion(kappa) > s[kappa - 1] { 57 | let kappa_prime = kappa; 58 | 59 | let index = (1..kappa) 60 | .rev() 61 | .find(|&k| delta_criterion(k) < s[k - 1]) 62 | .unwrap_or(0); 63 | 64 | let is_neg = s[index] <= zero; 65 | 66 | let k = if !is_neg { 67 | kappa = index; 68 | index 69 | } else { 70 | num_zeros += 1; 71 | kappa = kappa_prime; 72 | d - num_zeros 73 | }; 74 | 75 | basis.insert(kappa_prime, k); 76 | mu.insert(kappa_prime, k); 77 | r.insert(kappa_prime, k); 78 | 79 | // Update Gram matrix 80 | for i in 0..d { 81 | for j in 0..=i { 82 | gram[i][j] = basis[i].dot(&basis[j]); 83 | } 84 | } 85 | 86 | if is_neg { 87 | continue; 88 | } 89 | } 90 | r[kappa][kappa] = s[kappa].clone(); 91 | kappa += 1; 92 | } 93 | } 94 | 95 | /// Performs the `eta`-size-reduction of `basis[k]` 96 | /// 97 | /// Arguments: 98 | /// * `k`: Index of the column to be `eta`-size-reduced 99 | /// * `d`: The basis dimension 100 | /// * `basis`: A generating matrix for the lattice 101 | /// * `gram`: Gram matrix of `basis` 102 | /// * `mu`: Gram coefficient matrix 103 | /// * `r`: the r_ij matrix 104 | /// * `eta`: eta factor of the basis reduction 105 | /// 106 | /// Note: both `basis` and `gram` are updated by this operation. 107 | fn size_reduce( 108 | basis: &mut Matrix, 109 | gram: &mut Matrix, 110 | mu: &mut Matrix, 111 | r: &mut Matrix, 112 | m: &mut Vector, 113 | kappa: usize, 114 | eta: &S::Fraction, 115 | ) { 116 | let zero = S::Integer::from(0); 117 | let one = S::Integer::from(1); 118 | loop { 119 | cfa::(kappa, basis, gram, mu, r); 120 | 121 | let all_zeroes = (0..kappa) 122 | .rev() 123 | .all(|i| &S::abs(mu[kappa][i].clone()) < eta); 124 | 125 | if all_zeroes { 126 | break; 127 | } 128 | 129 | for i in 0..kappa { 130 | m[i] = mu[kappa][i].clone() 131 | } 132 | 133 | for i in (0..kappa).rev() { 134 | let x_i = S::round(&m[i]); 135 | if x_i != zero { 136 | for j in 0..i { 137 | m[j] -= 138 | &(mu[i][j].clone() * &S::Fraction::from_ext((x_i.clone(), one.clone()))); 139 | } 140 | 141 | // Swap basis 142 | basis[kappa] = basis[kappa].sub(&basis[i].mulf(x_i)); 143 | } 144 | } 145 | 146 | // Update Gram matrix 147 | 148 | for j in 0..=kappa { 149 | gram[kappa][j] = basis[kappa].dot(&basis[j]); 150 | } 151 | } 152 | } 153 | 154 | fn cfa( 155 | i: usize, 156 | basis: &mut Matrix, 157 | gram: &mut Matrix, 158 | mu: &mut Matrix, 159 | r: &mut Matrix, 160 | ) { 161 | for j in 0..=i { 162 | gram[i][j] = basis[i].dot(&basis[j]); 163 | } 164 | 165 | for j in 0..i { 166 | r[i][j] = S::Fraction::from_ext((gram[i][j].clone(), S::Integer::from(1))); 167 | 168 | for k in 0..j { 169 | r[i][j] = r[i][j].clone() - &(r[i][k].clone() * &mu[j][k]); 170 | } 171 | mu[i][j] = r[i][j].clone() / &r[j][j]; 172 | } 173 | } 174 | 175 | /// Puts the trailing null columns at the beginning of the matrix 176 | fn zeros_first(basis: &mut Matrix) { 177 | let (d, _) = basis.dimensions(); 178 | while basis[d - 1].is_zero() { 179 | basis.insert(d - 1, 0) 180 | } 181 | } 182 | 183 | fn reduction(basis: &mut Matrix, eta: f64, delta: f64) { 184 | lattice_reduce::(basis, eta, delta); 185 | lattice_reduce::(basis, eta, delta); 186 | zeros_first::(basis); 187 | } 188 | 189 | /// Lattice reduction (L² algorithm) 190 | /// 191 | /// This implementation uses `rug::Integers` and `rug::Rationnal` for the underlying arithmetic operations. 192 | /// 193 | /// Arguments: 194 | /// * basis: A generating matrix for the lattice 195 | /// * eta: eta factor of the basis reduction 196 | /// * delta: delta factor of the basis reduction 197 | /// 198 | /// The basis is reduced in-place. The reduction is performed according to the standard pipeline of the fplll implementation of LLL. 199 | /// It is done by doing one extra LLL-reduction at the end and putting all the trailing null rows at the beginning 200 | /// 201 | /// # Panics 202 | /// if delta <= 1/4 or delta >= 1 203 | /// if eta <= 1/2 or eta > sqrt(delta) 204 | pub fn lll_bignum(basis: &mut Matrix, eta: f64, delta: f64) { 205 | reduction::(basis, eta, delta) 206 | } 207 | 208 | /// Lattice reduction (L² algorithm) 209 | /// 210 | /// This implementation uses platform double floating-point numbers (IEEE 754) 211 | /// for the underlying arithmetic operations. 212 | /// 213 | /// Arguments: 214 | /// * basis: A generating matrix for the lattice 215 | /// * eta: eta factor of the basis reduction 216 | /// * delta: delta factor of the basis reduction 217 | /// 218 | /// The basis is reduced in-place. The reduction is performed according to the standard pipeline of the fplll implementation of LLL. 219 | /// It is done by doing one extra LLL-reduction at the end and putting all the trailing null rows at the beginning 220 | /// 221 | /// # Panics 222 | /// if delta <= 1/4 or delta >= 1 223 | /// if eta <= 1/2 or eta > sqrt(delta) 224 | pub fn lll_float(basis: &mut Matrix, eta: f64, delta: f64) { 225 | reduction::(basis, eta, delta) 226 | } 227 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A Rust implementation of the (basic) Lenstra-Lenstra-Lovasz lattice reduction algorithm 2 | //! 3 | //! # Introduction 4 | //! `lll-rs` is an implementation of the Lenstra-Lenstra-Lovász lattice basis reduction 5 | //! algorithm in Rust as well as the implementation of an improved version, the L² algorithm. 6 | //! The library comes with a set of simple helpers to create vectors and matrices to perform 7 | //! lattice basis reduction. 8 | //! 9 | //! # Examples 10 | //! 11 | //! ```rust 12 | //! use lll_rs::{ 13 | //! l2::{bigl2, l2f}, 14 | //! lll::{biglll, lllf}, 15 | //! matrix::Matrix, 16 | //! vector::{BigVector, VectorF}, 17 | //! }; 18 | //! 19 | //! use rug::{Integer,Assign}; 20 | //! 21 | //! // Init the matrix with Integer 22 | //! let mut basis: Matrix = Matrix::init(3, 4); 23 | //! 24 | //! // Populate the matix 25 | //! basis[0] = BigVector::from_vector(vec![ 26 | //! Integer::from(1) << 100000, 27 | //! Integer::from(0), 28 | //! Integer::from(0), 29 | //! Integer::from(1345), 30 | //! ]); 31 | //! basis[1] = BigVector::from_vector(vec![ 32 | //! Integer::from(0), 33 | //! Integer::from(1), 34 | //! Integer::from(0), 35 | //! Integer::from(35), 36 | //! ]); 37 | //! basis[2] = BigVector::from_vector(vec![ 38 | //! Integer::from(0), 39 | //! Integer::from(0), 40 | //! Integer::from(1), 41 | //! Integer::from(154), 42 | //! ]); 43 | //! 44 | //! // Perfom the LLL basis redution 45 | //! biglll::lattice_reduce(&mut basis); 46 | //! 47 | //! // OR 48 | //! // Perfom the LLL basis redution 49 | //! // Specify the delta and eta coefficient for the reduction 50 | //! bigl2::lattice_reduce(&mut basis, 0.5005, 0.999); 51 | //! ``` 52 | //! 53 | extern crate rug; 54 | 55 | mod algebra; 56 | pub mod l2; 57 | pub mod lll; 58 | 59 | pub use algebra::{BigNum, Float, Matrix}; 60 | 61 | #[cfg(test)] 62 | mod test { 63 | use crate::{l2, lll, Matrix}; 64 | 65 | #[test] 66 | fn test_lllf() { 67 | // "Bad" lattice basis 68 | let mut basis: Matrix = Matrix::from_matrix(vec![ 69 | vec![1., 0., 0., 1345.], 70 | vec![0., 1., 0., 35.], 71 | vec![0., 0., 1., 154.], 72 | ]); 73 | 74 | // "Good" lattice basis 75 | lll::lll_float(&mut basis); 76 | 77 | let result: Matrix = Matrix::from_matrix(vec![ 78 | vec![0.0, -4.0, 1.0, 14.0], 79 | vec![0.0, 1.0, 0.0, 35.0], 80 | vec![1.0, 348.0, -88.0, -27.0], 81 | ]); 82 | 83 | assert_eq!(basis, result); 84 | } 85 | 86 | #[test] 87 | fn test_biglll() { 88 | type I = rug::Integer; 89 | // "Bad" lattice basis 90 | let mut basis: Matrix = Matrix::from_matrix(vec![ 91 | vec![I::from(1) << 100000, I::from(0), I::from(0), I::from(1345)], 92 | vec![I::from(0), I::from(1), I::from(0), I::from(35)], 93 | vec![I::from(0), I::from(0), I::from(1), I::from(154)], 94 | ]); 95 | println!("{:?}", basis); 96 | 97 | // "Good" lattice basis 98 | lll::lll_bignum(&mut basis); 99 | println!("{:?}", basis); 100 | } 101 | 102 | #[test] 103 | fn test_l2f() { 104 | // "Bad" lattice basis 105 | let mut basis: Matrix = Matrix::from_matrix(vec![ 106 | vec![1., 0., 0., 1345.], 107 | vec![0., 1., 0., 35.], 108 | vec![0., 0., 1., 154.], 109 | ]); 110 | println!("{:?}", basis); 111 | 112 | // "Good" lattice basis 113 | l2::lll_float(&mut basis, 0.501, 0.998); 114 | println!("{:?}", basis); 115 | 116 | let result: Matrix = Matrix::from_matrix(vec![ 117 | vec![1.0, 1.0, -9.0, -6.0], 118 | vec![0.0, 9.0, -2.0, 7.0], 119 | vec![1.0, -3.0, -8.0, 8.0], 120 | ]); 121 | 122 | assert_eq!(basis, result); 123 | } 124 | 125 | #[test] 126 | fn test_bigl2() { 127 | type I = rug::Integer; 128 | let mut basis: Matrix = Matrix::from_matrix(vec![ 129 | vec![I::from(1), I::from(2), I::from(3)], 130 | vec![I::from(4), I::from(5), I::from(6)], 131 | vec![I::from(7), I::from(8), I::from(9)], 132 | ]); 133 | println!("{:?}", basis); 134 | 135 | l2::lll_bignum(&mut basis, 0.6, 0.95); 136 | println!("{:?}", basis); 137 | 138 | let result: Matrix = Matrix::from_matrix(vec![ 139 | vec![I::from(0), I::from(0), I::from(0)], 140 | vec![I::from(2), I::from(1), I::from(0)], 141 | vec![I::from(-1), I::from(1), I::from(3)], 142 | ]); 143 | assert_eq!(basis, result); 144 | } 145 | 146 | #[test] 147 | fn test_bigl2_ntrulike() { 148 | type I = rug::Integer; 149 | let mut basis: Matrix = Matrix::from_matrix(vec![ 150 | vec![ 151 | I::from(1), 152 | I::from(0), 153 | I::from(0), 154 | I::from(436), 155 | I::from(225), 156 | I::from(381), 157 | ], 158 | vec![ 159 | I::from(0), 160 | I::from(1), 161 | I::from(0), 162 | I::from(381), 163 | I::from(436), 164 | I::from(225), 165 | ], 166 | vec![ 167 | I::from(0), 168 | I::from(0), 169 | I::from(1), 170 | I::from(225), 171 | I::from(381), 172 | I::from(436), 173 | ], 174 | vec![ 175 | I::from(0), 176 | I::from(0), 177 | I::from(0), 178 | I::from(521), 179 | I::from(0), 180 | I::from(0), 181 | ], 182 | vec![ 183 | I::from(0), 184 | I::from(0), 185 | I::from(0), 186 | I::from(0), 187 | I::from(521), 188 | I::from(0), 189 | ], 190 | vec![ 191 | I::from(0), 192 | I::from(0), 193 | I::from(0), 194 | I::from(0), 195 | I::from(0), 196 | I::from(521), 197 | ], 198 | ]); 199 | println!("{:?}", basis); 200 | 201 | l2::lll_bignum(&mut basis, 0.6, 0.95); 202 | println!("{:?}", basis); 203 | 204 | let result: Matrix = Matrix::from_matrix(vec![ 205 | vec![ 206 | I::from(1), 207 | I::from(1), 208 | I::from(1), 209 | I::from(0), 210 | I::from(0), 211 | I::from(0), 212 | ], 213 | vec![ 214 | I::from(-11), 215 | I::from(0), 216 | I::from(12), 217 | I::from(-12), 218 | I::from(13), 219 | I::from(-1), 220 | ], 221 | vec![ 222 | I::from(12), 223 | I::from(-11), 224 | I::from(0), 225 | I::from(-1), 226 | I::from(-12), 227 | I::from(13), 228 | ], 229 | vec![ 230 | I::from(12), 231 | I::from(-1), 232 | I::from(-10), 233 | I::from(-4), 234 | I::from(17), 235 | I::from(-13), 236 | ], 237 | vec![ 238 | I::from(1), 239 | I::from(10), 240 | I::from(-12), 241 | I::from(-17), 242 | I::from(13), 243 | I::from(4), 244 | ], 245 | vec![ 246 | I::from(8), 247 | I::from(-5), 248 | I::from(-4), 249 | I::from(162), 250 | I::from(180), 251 | I::from(179), 252 | ], 253 | ]); 254 | assert_eq!(basis, result); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/lll/mod.rs: -------------------------------------------------------------------------------- 1 | //! The Lenstra-Lenstra-Lovasz algorithm [LLL82] 2 | 3 | use crate::algebra::{BigNum, Float, FromExt, Matrix, Scalar}; 4 | 5 | /// Lattice reduction using the original Lenstra-Lenstra-Lovasz algorithm 6 | /// 7 | /// This implementation uses generic Scalars for arithmetic operations. 8 | /// The value of `delta` is set to 0.75. 9 | /// 10 | /// - `basis`: A generating matrix for the lattice 11 | /// 12 | /// The basis is reduced in-place. 13 | fn lattice_reduce(basis: &mut Matrix) { 14 | // Parameter delta in the Lovasz condition 15 | let delta = S::Fraction::from_ext((3, 4)); 16 | 17 | let (n, _) = basis.dimensions(); 18 | let mut swap_condition = true; 19 | 20 | while swap_condition { 21 | // Perform rounded Gram-Schmidt orthogonalisation 22 | for i in 0..n { 23 | for k in 1..i { 24 | let j = i - k; 25 | 26 | let b_i = &basis[i]; 27 | let b_j = &basis[j]; 28 | let alpha = S::round_div(b_i.dot(b_j), b_j.dot(b_j)); 29 | basis[i] = b_i.sub(&b_j.mulf(alpha)); 30 | } 31 | } 32 | 33 | // Check for the Lovasz condition and swap columns if appropriate 34 | swap_condition = false; 35 | for i in 0..n - 1 { 36 | let b_i = &basis[i]; 37 | let b_ip1 = &basis[i + 1]; 38 | 39 | let lhs = S::Fraction::from_ext(&b_i.dot(b_i)) * δ 40 | 41 | let alpha = S::round_div(b_ip1.dot(b_i), b_i.dot(b_i)); 42 | let vec_rhs = b_ip1.add(&b_i.mulf(alpha)); 43 | let rhs = vec_rhs.dot(&vec_rhs); 44 | 45 | if lhs > rhs { 46 | basis.swap(i, i + 1); 47 | swap_condition = true; 48 | break; 49 | } 50 | } 51 | } 52 | } 53 | 54 | /// Lattice reduction using the original Lenstra-Lenstra-Lovasz algorithm 55 | /// 56 | /// This implementation uses generic `rug::Integer` and `rug::Fraction` for arithmetic operations. 57 | /// The value of `delta` is set to 0.75. 58 | /// 59 | /// - `basis`: A generating matrix for the lattice 60 | /// 61 | /// The basis is reduced in-place. 62 | #[deprecated( 63 | note = "Current implementation might yield incorrect results. Use l2.lll_bignum() instead" 64 | )] 65 | pub fn lll_bignum(basis: &mut Matrix) { 66 | lattice_reduce::(basis) 67 | } 68 | 69 | /// Lattice reduction using the original Lenstra-Lenstra-Lovasz algorithm 70 | /// 71 | /// This implementation uses platform double floating-point numbers (IEEE 754) for arithmetic operations. 72 | /// The value of `delta` is set to 0.75. 73 | /// 74 | /// - `basis`: A generating matrix for the lattice 75 | /// 76 | /// The basis is reduced in-place. 77 | #[deprecated( 78 | note = "Current implementation might yield incorrect results. Use l2.lll_float() instead" 79 | )] 80 | pub fn lll_float(basis: &mut Matrix) { 81 | lattice_reduce::(basis) 82 | } 83 | --------------------------------------------------------------------------------