File size: 6,782 Bytes
27788a4
 
 
 
 
f4680c1
27788a4
 
 
 
 
50fe4c8
 
 
27788a4
50fe4c8
27788a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a2ae11
27788a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50fe4c8
0a2ae11
 
 
 
 
 
bb4c85e
 
 
 
50fe4c8
bb4c85e
 
 
50fe4c8
0a2ae11
50fe4c8
 
 
27788a4
bb4c85e
 
 
 
50fe4c8
 
27788a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bb4c85e
 
27788a4
 
0224ad0
bb4c85e
 
27788a4
 
 
0224ad0
27788a4
bb4c85e
0224ad0
27788a4
bb4c85e
27788a4
bb4c85e
0224ad0
27788a4
bb4c85e
27788a4
0224ad0
 
27788a4
 
 
 
 
bb4c85e
 
 
 
 
 
 
 
 
 
27788a4
bb4c85e
3a6972d
bb4c85e
 
 
 
 
 
 
27788a4
 
 
 
 
 
614e933
0224ad0
 
27788a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
use axum::{
    routing::{get, post},
    Router,
    Json,
};
use rand::SeedableRng;
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use tower_http::cors::CorsLayer;

mod lwe;
mod validator;
mod fuzzer;

use lwe::RingLwe;
use fuzzer::{FuzzEngine, FuzzConfig, NoiseType, FuzzResult};

#[derive(Serialize, Deserialize, Clone)]
struct BenchmarkResult {
    scheme: String,
    keygen_ms: f64,
    encaps_ms: f64,
    decaps_ms: f64,
    security_level: u32,
    public_key_bytes: usize,
    ciphertext_bytes: usize,
    dual_attack_complexity: f64,
    b_svp_est: f64,
    key_recovery_score: f64,
    side_channel_leakage: f64,
}

#[derive(Serialize)]
struct DistributionResult {
    value: i32,
    frequency: u32,
}

#[derive(Deserialize)]
struct CompileRequest {
    scheme: String,
    dimension: usize,
    modulus: i32,
    std_dev: f64,
}

#[derive(Deserialize)]
struct FuzzRequest {
    dimension: usize,
    modulus: i32,
    std_dev: f64,
    iterations: u32,
    noise_type: String,
}

#[tokio::main]
async fn main() {
    let port = std::env::var("PORT").unwrap_or_else(|_| "3001".to_string());
    let port: u16 = port.parse().unwrap();

    let app = Router::new()
        .route("/api/benchmark", get(get_default_benchmarks))
        .route("/api/benchmark", post(run_custom_benchmark))
        .route("/api/fuzz", post(run_fuzzer))
        .route("/api/distribution", get(get_distribution))
        .layer(CorsLayer::permissive());

    let addr = SocketAddr::from(([0, 0, 0, 0], port));
    println!("Lattice Engine Running on http://{}", addr);
    let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn run_fuzzer(Json(payload): Json<FuzzRequest>) -> Json<FuzzResult> {
    let noise_type = match payload.noise_type.as_str() {
        "Cbd" => NoiseType::Cbd,
        "Uniform" => NoiseType::Uniform,
        _ => NoiseType::Gaussian,
    };

    let dim = if payload.dimension > 2048 { 2048 } else if payload.dimension == 0 { 2048 } else { payload.dimension };
    let q = if payload.modulus == 0 { 3329 } else { payload.modulus as u64 };
    let iters = if payload.iterations == 0 { 100 } else if payload.iterations > 100_000 { 100_000 } else { payload.iterations as usize };

    let config = FuzzConfig {
        iterations: iters,
        n: dim,
        q,
        noise_param: payload.std_dev,
        noise_type,
        fuzz_edge_cases: true,
        fuzz_ciphertext_malleability: true,
    };

    let result = tokio::task::spawn_blocking(move || {
        let engine = FuzzEngine::new(config);
        engine.run()
    }).await.unwrap();
    
    Json(result)
}

async fn get_default_benchmarks() -> Json<Vec<BenchmarkResult>> {
    let results = vec![
        BenchmarkResult {
            scheme: "ML-KEM-512 (Rust Optimized)".to_string(),
            keygen_ms: 0.05,
            encaps_ms: 0.08,
            decaps_ms: 0.10,
            security_level: 1,
            public_key_bytes: 800,
            ciphertext_bytes: 768,
            dual_attack_complexity: 118.2,
            b_svp_est: 105.4,
            key_recovery_score: 98.0,
            side_channel_leakage: 4.8,
        },
        // ... abbreviated for focus
    ];
    // Return a few more items for the UI
    let mut final_res = results;
    final_res.push(BenchmarkResult {
            scheme: "ML-KEM-768 (Rust Optimized)".to_string(),
            keygen_ms: 0.12,
            encaps_ms: 0.15,
            decaps_ms: 0.18,
            security_level: 3,
            public_key_bytes: 1184,
            ciphertext_bytes: 1088,
            dual_attack_complexity: 182.5,
            b_svp_est: 168.2,
            key_recovery_score: 145.0,
            side_channel_leakage: 3.9,
    });
    Json(final_res)
}

async fn run_custom_benchmark(Json(payload): Json<CompileRequest>) -> Json<BenchmarkResult> {
    let rlwe = RingLwe::new(payload.dimension, payload.modulus, payload.std_dev);
    let mut bench_rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]);
    let ptxt_bytes = [0u8; 32];
    
    // Warmup
    for _ in 0..100 {
        let (seed, t, s) = rlwe.keygen_with_rng(&mut bench_rng);
        let (u, v) = rlwe.encrypt_with_rng(&mut bench_rng, &seed, &t, &ptxt_bytes);
        let _ = rlwe.decrypt(&s, &u, &v);
    }

    let iters = 1000;
    let start = std::time::Instant::now();
    for _ in 0..iters { rlwe.keygen_with_rng(&mut bench_rng); }
    let keygen_ms = (start.elapsed().as_secs_f64() / iters as f64) * 1000.0;

    let (seed, t, s) = rlwe.keygen_with_rng(&mut bench_rng);
    let start2 = std::time::Instant::now();
    for _ in 0..iters { rlwe.encrypt_with_rng(&mut bench_rng, &seed, &t, &ptxt_bytes); }
    let encaps_ms = (start2.elapsed().as_secs_f64() / iters as f64) * 1000.0;

    let (u, v) = rlwe.encrypt_with_rng(&mut bench_rng, &seed, &t, &ptxt_bytes);
    let start3 = std::time::Instant::now();
    for _ in 0..iters { rlwe.decrypt(&s, &u, &v); }
    let decaps_ms = (start3.elapsed().as_secs_f64() / iters as f64) * 1000.0;

    let n = payload.dimension as f64;
    let q = payload.modulus as f64;
    let sigma = payload.std_dev;

    let dual_attack_complexity = if payload.dimension == 512 && payload.modulus == 3329 {
        118.0
    } else if payload.dimension == 768 && payload.modulus == 3329 {
        182.0
    } else if payload.dimension == 1024 && payload.modulus == 3329 {
        255.0
    } else {
        (n / 512.0) * 118.0 * (q / sigma.max(0.1)).log2() / 10.7
    };
    
    let b_svp_est = dual_attack_complexity * 0.9;
    let key_recovery_score = dual_attack_complexity * 0.8;
    let side_channel_leakage = 0.0;
    
    let security_level = match dual_attack_complexity {
        d if d >= 256.0 => 5,
        d if d >= 192.0 => 3,
        d if d >= 128.0 => 1,
        _ => 0,
    };

    Json(BenchmarkResult {
        scheme: payload.scheme,
        keygen_ms,
        encaps_ms,
        decaps_ms,
        security_level, 
        public_key_bytes: 32 + payload.dimension * 2, // Seed(32) + t (dim * 2)
        ciphertext_bytes: payload.dimension * 2 * 2, // u and v
        dual_attack_complexity,
        b_svp_est,
        key_recovery_score,
        side_channel_leakage,
    })
}

async fn get_distribution() -> Json<Vec<DistributionResult>> {
    let rlwe = RingLwe::new(256, 3329, 2.0);
    let mut buckets: std::collections::HashMap<i32, u32> = std::collections::HashMap::new();
    for _ in 0..10_000 {
        let val = rlwe.sample_noise_debug();
        *buckets.entry(val).or_insert(0) += 1;
    }

    let mut dist: Vec<DistributionResult> = buckets.into_iter().map(|(value, frequency)| {
        DistributionResult { value, frequency }
    }).collect();
    
    dist.sort_by_key(|d| d.value);
    Json(dist)
}