crypto-async-rs 0.1.3

High-performance pure Rust cryptographic library with async streaming support
Documentation
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
//! # X25519 Elliptic Curve Diffie-Hellman Key Exchange
//!
//! This module provides a pure Rust implementation of the X25519 elliptic curve
//! Diffie-Hellman key exchange algorithm as specified in RFC 7748.
//!
//! ## Features
//!
//! - Constant-time implementation resistant to timing attacks
//! - Secure memory clearing using custom Drop implementation
//! - Input validation and proper error handling
//! - High-performance implementation with inline optimizations
//!
//! ## Security Considerations
//!
//! - All operations are implemented in constant time to prevent timing attacks
//! - Sensitive data is automatically zeroed when dropped
//! - Input validation prevents invalid key material from being processed
//!
//! ## Example
//!
//! ```rust
//! use crypto_async_rs::ecdh_x25519::{x25519, U_COORDINATE};
//!
//! // Example private keys (in practice, these should be randomly generated)
//! let alice_private = [
//!     0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72, 0x51, 0xb2, 0x66, 0x45,
//!     0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a, 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a,
//! ];
//! let bob_private = [
//!     0x5d, 0xab, 0x08, 0x7e, 0x62, 0x4a, 0x8a, 0x4b, 0x79, 0xe1, 0x7f, 0x8b, 0x83, 0x80, 0x0e, 0xe6,
//!     0x6f, 0x3b, 0xb1, 0x29, 0x26, 0x18, 0xb6, 0xfd, 0x1c, 0x2f, 0x8b, 0x27, 0xff, 0x88, 0xe0, 0xeb,
//! ];
//!
//! // Compute public keys
//! let alice_public = x25519(alice_private, U_COORDINATE)?;
//! let bob_public = x25519(bob_private, U_COORDINATE)?;
//!
//! // Perform key exchange
//! let alice_shared = x25519(alice_private, bob_public)?;
//! let bob_shared = x25519(bob_private, alice_public)?;
//!
//! // Both parties now have the same shared secret
//! assert_eq!(alice_shared, bob_shared);
//! # Ok::<(), crypto_async_rs::ecdh_x25519::X25519Error>(())
//! ```


/// Error types for X25519 operations
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum X25519Error {
    /// Invalid input: scalar or u-coordinate is all zeros
    InvalidInput,
    /// Input validation failed
    ValidationError(String),
}

impl std::fmt::Display for X25519Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            X25519Error::InvalidInput => write!(f, "Invalid input: scalar or u-coordinate is all zeros"),
            X25519Error::ValidationError(msg) => write!(f, "Validation error: {}", msg),
        }
    }
}

impl std::error::Error for X25519Error {}

/// Result type for X25519 operations
pub type X25519Result<T> = Result<T, X25519Error>;

const CURVE25519_BIT_LEN: usize = 255;
pub const CURVE25519_BYTE_LEN: usize = 32;
const CURVE25519_WORD_LEN: usize = 8;
const CURVE25519_A24: u32 = 121666;

/// The base point U-coordinate for X25519 (9 in little-endian format)
pub const U_COORDINATE: [u8; CURVE25519_BYTE_LEN] = [
    9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];


#[repr(align(32))]
struct X25519State {
    k: [u32; CURVE25519_WORD_LEN],
    u: [u32; CURVE25519_WORD_LEN],
    x1: [u32; CURVE25519_WORD_LEN],
    z1: [u32; CURVE25519_WORD_LEN],
    x2: [u32; CURVE25519_WORD_LEN],
    z2: [u32; CURVE25519_WORD_LEN],
    t1: [u32; CURVE25519_WORD_LEN],
    t2: [u32; CURVE25519_WORD_LEN],
}

impl Drop for X25519State {
    fn drop(&mut self) {
        let raw = self as *mut X25519State as *mut [u8; 256];
        unsafe { *raw = [0; 256] };
    }
}

/// Validates that the input is not all zeros
#[inline]
fn validate_input(input: &[u8; CURVE25519_BYTE_LEN]) -> X25519Result<()> {
    let is_zero = input.iter().all(|&b| b == 0);
    if is_zero {
        return Err(X25519Error::InvalidInput);
    }
    Ok(())
}

/// Safely converts a byte array to a u32 array using little-endian interpretation
#[inline]
fn bytes_to_u32_array(bytes: [u8; CURVE25519_BYTE_LEN]) -> [u32; CURVE25519_WORD_LEN] {
    let mut result = [0u32; CURVE25519_WORD_LEN];
    for (i, chunk) in bytes.chunks_exact(4).enumerate() {
        result[i] = u32::from_le_bytes(chunk.try_into().unwrap());
    }
    result
}

/// Safely converts a u32 array to a byte array using little-endian interpretation
#[inline]
fn u32_array_to_bytes(array: [u32; CURVE25519_WORD_LEN]) -> [u8; CURVE25519_BYTE_LEN] {
    let mut result = [0u8; CURVE25519_BYTE_LEN];
    for (i, &word) in array.iter().enumerate() {
        let bytes = word.to_le_bytes();
        result[i * 4..(i + 1) * 4].copy_from_slice(&bytes);
    }
    result
}

/// Performs X25519 scalar multiplication
///
/// # Arguments
/// * `k` - The scalar (private key) as a 32-byte array
/// * `u` - The u-coordinate (public key) as a 32-byte array
///
/// # Returns
/// * `Ok([u8; 32])` - The resulting shared secret
/// * `Err(X25519Error)` - If input validation fails
///
/// # Errors
/// * `X25519Error::InvalidInput` - If either input is all zeros
///
/// # Security
/// This function implements constant-time operations to prevent timing attacks.
pub fn x25519(k: [u8; CURVE25519_BYTE_LEN], u: [u8; CURVE25519_BYTE_LEN]) -> X25519Result<[u8; CURVE25519_BYTE_LEN]> {
    // Validate inputs
    validate_input(&k)?;
    validate_input(&u)?;

    let mut swap: u32 = 0;
    let mut b: u32;
    let mut state = X25519State {
        k: [0; CURVE25519_WORD_LEN],
        u: [0; CURVE25519_WORD_LEN],
        x1: [0; CURVE25519_WORD_LEN],
        z1: [0; CURVE25519_WORD_LEN],
        x2: [0; CURVE25519_WORD_LEN],
        z2: [0; CURVE25519_WORD_LEN],
        t1: [0; CURVE25519_WORD_LEN],
        t2: [0; CURVE25519_WORD_LEN],
    };

    // Copy scalar using safe conversion
    state.k = bytes_to_u32_array(k);
    
    // Set the three least significant bits of the first byte and the most
    // significant bit of the last to zero, set the second most significant
    // bit of the last byte to 1
    state.k[0] &= 0xFFFFFFF8;
    state.k[7] &= 0x7FFFFFFF;
    state.k[7] |= 0x40000000;

    // Copy input u-coordinate using safe conversion
    state.u = bytes_to_u32_array(u);

    // Implementations must mask the most significant bit in the final byte
    state.u[7] &= 0x7FFFFFFF;

    // Implementations must accept non-canonical values and process them as
    // if they had been reduced modulo the field prime (refer to RFC 7748,
    // section 5)
    state.u = curve25519_red(state.u);

    // Set Z1 = 0
    // Set X1 = 1
    state.x1[0] = 1;
    // Set X2 = U
    state.x2 = state.u;
    // Set Z2 = 1
    state.z2[0] = 1;

    // Montgomery ladder
    for i in (0usize..CURVE25519_BIT_LEN).rev() {
        // The scalar is processed in a left-to-right fashion
        b = (state.k[i / 32] >> (i % 32)) & 1;

        // Conditional swap
        curve25519_swap(&mut state.x1, &mut state.x2, swap ^ b);
        curve25519_swap(&mut state.z1, &mut state.z2, swap ^ b);

        // Save current bit value
        swap = b;

        // Compute T1 = X2 + Z2
        state.t1 = curve25519_add(state.x2, state.z2);
        // Compute X2 = X2 - Z2
        state.x2 = curve25519_sub(state.x2, state.z2);
        // Compute Z2 = X1 + Z1
        state.z2 = curve25519_add(state.x1, state.z1);
        // Compute X1 = X1 - Z1
        state.x1 = curve25519_sub(state.x1, state.z1);
        // Compute T1 = T1 * X1
        state.t1 = curve25519_mul(state.t1, state.x1);
        // Compute X2 = X2 * Z2
        state.x2 = curve25519_mul(state.x2, state.z2);
        // Compute Z2 = Z2 * Z2
        state.z2 = curve25519_sqr(state.z2);
        // Compute X1 = X1 * X1
        state.x1 = curve25519_sqr(state.x1);
        // Compute T2 = Z2 - X1
        state.t2 = curve25519_sub(state.z2, state.x1);
        // Compute Z1 = T2 * a24
        state.z1 = curve25519_mul_int(state.t2, CURVE25519_A24);
        // Compute Z1 = Z1 + X1
        state.z1 = curve25519_add(state.z1, state.x1);
        // Compute Z1 = Z1 * T2
        state.z1 = curve25519_mul(state.z1, state.t2);
        // Compute X1 = X1 * Z2
        state.x1 = curve25519_mul(state.x1, state.z2);
        // Compute Z2 = T1 - X2
        state.z2 = curve25519_sub(state.t1, state.x2);
        // Compute Z2 = Z2 * Z2
        state.z2 = curve25519_sqr(state.z2);
        // Compute Z2 = Z2 * U
        state.z2 = curve25519_mul(state.z2, state.u);
        // Compute X2 = X2 + T1
        state.x2 = curve25519_add(state.x2, state.t1);
        // Compute X2 = X2 * X2
        state.x2 = curve25519_sqr(state.x2);
    }

    // Conditional swap
    curve25519_swap(&mut state.x1, &mut state.x2, swap);
    curve25519_swap(&mut state.z1, &mut state.z2, swap);

    // Retrieve affine representation
    state.u = curve25519_inv(state.z1);
    state.u = curve25519_mul(state.u, state.x1);

    Ok(u32_array_to_bytes(state.u))
}

/// Modular reduction
/// 
/// Performs modular reduction modulo the Curve25519 prime p = 2^255 - 19
#[inline]
fn curve25519_red(a: [u32; CURVE25519_WORD_LEN]) -> [u32; CURVE25519_WORD_LEN] {
    let mut temp: u64 = 19;
    let mut b: [u32; CURVE25519_WORD_LEN] = Default::default();

    // Compute B = A + 19
    for i in 0..CURVE25519_WORD_LEN {
        temp += a[i] as u64;
        b[i] = temp as u32;
        temp >>= 32;
    }

    // Compute B = A - (2^255 - 19)
    b[7] = b[7].wrapping_sub(0x80000000);
    // If B < (2^255 - 19) then R = B, else R = A
    curve25519_select(&b, &a, (b[7] & 0x80000000) >> 31)
}

/// Select an integer based on a condition
/// 
/// Performs constant-time selection between two integers
#[inline]
fn curve25519_select(a: &[u32; CURVE25519_WORD_LEN], b: &[u32; CURVE25519_WORD_LEN], c: u32) -> [u32; CURVE25519_WORD_LEN] {
    // The mask is the all-1 or all-0 word
    let mask = c.wrapping_sub(1);
    let mut r: [u32; CURVE25519_WORD_LEN] = Default::default();
    // Select between A and B
    for i in 0..CURVE25519_WORD_LEN {
        // Constant time implementation
        r[i] = (a[i] & mask) | (b[i] & !mask);
    }

    r
}

/// Conditional swap
/// 
/// Performs constant-time conditional swap of two integers
#[inline]
fn curve25519_swap(a: &mut [u32; CURVE25519_WORD_LEN], b: &mut [u32; CURVE25519_WORD_LEN], c: u32) {
    let mut dummy: u32;
    // The mask is the all-1 or all-0 word
    let mask = (!c).wrapping_add(1);

    for i in 0..CURVE25519_WORD_LEN {
        dummy = mask & (a[i] ^ b[i]);
        a[i] ^= dummy;
        b[i] ^= dummy;
    }
}

/// Modular addition
/// 
/// Performs modular addition: R = (A + B) mod p
#[inline]
fn curve25519_add(a: [u32; CURVE25519_WORD_LEN], b: [u32; CURVE25519_WORD_LEN]) -> [u32; CURVE25519_WORD_LEN] {
    let mut temp: u64 = 0;
    let mut r: [u32; CURVE25519_WORD_LEN] = Default::default();

    // Compute R = A + B
    for i in 0..CURVE25519_WORD_LEN {
        temp += a[i] as u64;
        temp += b[i] as u64;
        r[i] = temp as u32;
        temp >>= 32;
    }

    // Perform modular reduction
    curve25519_red(r)
}

/// Modular subtraction
/// 
/// Performs modular subtraction: R = (A - B) mod p
#[inline]
fn curve25519_sub(a: [u32; CURVE25519_WORD_LEN], b: [u32; CURVE25519_WORD_LEN]) -> [u32; CURVE25519_WORD_LEN] {
    let mut temp: i64 = -19;
    let mut result: [u32; CURVE25519_WORD_LEN] = Default::default();

    // Compute R = A - 19 - B
    for i in 0..CURVE25519_WORD_LEN {
        temp += a[i] as i64;
        temp -= b[i] as i64;
        result[i] = temp as u32;
        temp >>= 32;
    }

    // Compute R = A + (2^255 - 19) - B
    result[7] = result[7].wrapping_add(0x80000000);

    // Perform modular reduction
    curve25519_red(result)
}

/// Modular multiplication
/// 
/// Performs modular multiplication: R = (A * B) mod p
#[inline]
fn curve25519_mul(a: [u32; CURVE25519_WORD_LEN], b: [u32; CURVE25519_WORD_LEN]) -> [u32; CURVE25519_WORD_LEN] {
    let mut c: u64 = 0;
    let mut temp: u64 = 0;
    let mut u: [u32; 16] = Default::default();

    // Comba's method is used to perform multiplication
    for i in 0..16 {
        // The algorithm computes the products, column by column
        if i < CURVE25519_WORD_LEN {
            // Inner loop
            for j in 0..=i {
                temp += a[j] as u64 * b[i - j] as u64;
                c += temp >> 32;
                temp &= 0xFFFFFFFF;
            }
        } else {
            // Inner loop
            for j in i - 7..CURVE25519_WORD_LEN {
                temp += a[j] as u64 * b[i - j] as u64;
                c += temp >> 32;
                temp &= 0xFFFFFFFF;
            }
        }

        // At the bottom of each column, the final result is written to memory
        u[i] = temp as u32;

        // Propagate the carry upwards
        temp = c & 0xFFFFFFFF;
        c >>= 32;
    }

    // Reduce bit 255 (2^255 = 19 mod p)
    temp = (u[7] >> 31) as u64 * 19;
    // Mask the most significant bit
    u[7] &= 0x7FFFFFFF;

    // Perform fast modular reduction (first pass)
    for i in 0..CURVE25519_WORD_LEN {
        temp += u[i] as u64;
        temp += u[i + CURVE25519_WORD_LEN] as u64 * 38;
        u[i] = temp as u32;
        temp >>= 32;
    }

    // Reduce bit 256 (2^256 = 38 mod p)
    temp *= 38;
    // Reduce bit 255 (2^255 = 19 mod p)
    temp += (u[7] >> 31) as u64 * 19;
    // Mask the most significant bit
    u[7] &= 0x7FFFFFFF;

    // Perform fast modular reduction (second pass)
    for i in 0..CURVE25519_WORD_LEN {
        temp += u[i] as u64;
        u[i] = temp as u32;
        temp >>= 32;
    }

    // Reduce non-canonical values
    let mut temp: [u32; CURVE25519_WORD_LEN] = Default::default();
    temp.copy_from_slice(&u[..CURVE25519_WORD_LEN]);
    curve25519_red(temp)
}

/// Modular squaring
/// 
/// Performs modular squaring: R = (A^2) mod p
#[inline]
fn curve25519_sqr(a: [u32; CURVE25519_WORD_LEN]) -> [u32; CURVE25519_WORD_LEN] {
    // Compute R = (A^2) mod p
    curve25519_mul(a, a)
}

/// Modular multiplication by integer
/// 
/// Performs modular multiplication: R = (A * B) mod p where B is a 32-bit integer
#[inline]
fn curve25519_mul_int(a: [u32; CURVE25519_WORD_LEN], b: u32) -> [u32; CURVE25519_WORD_LEN] {
    let mut temp: u64 = 0;
    let mut u: [u32; CURVE25519_WORD_LEN] = Default::default();

    // Compute R = A * B
    for i in 0..CURVE25519_WORD_LEN {
        temp += a[i] as u64 * b as u64;
        u[i] = temp as u32;
        temp >>= 32;
    }

    // Reduce bit 256 (2^256 = 38 mod p)
    temp *= 38;
    // Reduce bit 255 (2^255 = 19 mod p)
    temp += (u[7] >> 31) as u64 * 19;
    // Mask the most significant bit
    u[7] &= 0x7FFFFFFF;

    // Perform fast modular reduction
    for i in 0..CURVE25519_WORD_LEN {
        temp += u[i] as u64;
        u[i] = temp as u32;
        temp >>= 32;
    }

    // Reduce non-canonical values
    curve25519_red(u)
}



/// Modular multiplicative inverse
/// 
/// Performs modular multiplicative inverse: R = A^-1 mod p
#[inline]
fn curve25519_inv(a: [u32; CURVE25519_WORD_LEN]) -> [u32; CURVE25519_WORD_LEN] {
    let mut u: [u32; CURVE25519_WORD_LEN];
    let mut v: [u32; CURVE25519_WORD_LEN];

    // Since GF(p) is a prime field, the Fermat's little theorem can be
    // used to find the multiplicative inverse of A modulo p
    u = curve25519_sqr(a);
    u = curve25519_mul(u, a); // A^(2^2 - 1)
    u = curve25519_sqr(u);
    v = curve25519_mul(u, a); // A^(2^3 - 1)

    u = curve25519_pwr2(v, 3);
    u = curve25519_mul(u, v); // A^(2^6 - 1)
    u = curve25519_sqr(u);
    v = curve25519_mul(u, a); // A^(2^7 - 1)

    u = curve25519_pwr2(v, 7);
    u = curve25519_mul(u, v); // A^(2^14 - 1)
    u = curve25519_sqr(u);
    v = curve25519_mul(u, a); // A^(2^15 - 1)

    u = curve25519_pwr2(v, 15);
    u = curve25519_mul(u, v); // A^(2^30 - 1)
    u = curve25519_sqr(u);
    v = curve25519_mul(u, a); // A^(2^31 - 1)

    u = curve25519_pwr2(v, 31);
    v = curve25519_mul(u, v); // A^(2^62 - 1)

    u = curve25519_pwr2(v, 62);
    u = curve25519_mul(u, v); // A^(2^124 - 1)
    u = curve25519_sqr(u);
    v = curve25519_mul(u, a); // A^(2^125 - 1)

    u = curve25519_pwr2(v, 125);
    u = curve25519_mul(u, v); // A^(2^250 - 1)
    u = curve25519_sqr(u);
    u = curve25519_sqr(u);
    u = curve25519_mul(u, a);
    u = curve25519_sqr(u);
    u = curve25519_sqr(u);
    u = curve25519_mul(u, a);
    u = curve25519_sqr(u);
    curve25519_mul(u, a) // A^(2^255 - 21)
}

/// Raise an integer to power 2^n
/// 
/// Performs modular exponentiation: R = (A^(2^n)) mod p
#[inline]
fn curve25519_pwr2(a: [u32; CURVE25519_WORD_LEN], n: usize) -> [u32; CURVE25519_WORD_LEN] {
    // Pre-compute (A^2) mod p
    let mut result = curve25519_sqr(a);

    // Compute R = (A^(2^n)) mod p
    for _ in 1..n {
        result = curve25519_sqr(result);
    }

    result
}

#[cfg(test)]
/// Generates a random private key for X25519
///
/// # Arguments
/// * `rng` - A cryptographically secure random number generator
///
/// # Returns
/// * `Ok([u8; 32])` - A valid private key
/// * `Err(X25519Error)` - If key generation fails
///
/// # Example
/// ```rust,ignore
/// use sha::ecdh_x25519::generate_private_key;
/// use rand::rngs::OsRng;
///
/// let private_key = generate_private_key(&mut OsRng)?;
/// # Ok::<(), sha::ecdh_x25519::X25519Error>(())
/// ```
pub fn generate_private_key<R: rand::RngCore + rand::CryptoRng>(rng: &mut R) -> X25519Result<[u8; CURVE25519_BYTE_LEN]> {
    let mut key = [0u8; CURVE25519_BYTE_LEN];
    rng.fill_bytes(&mut key);
    
    // Ensure the key is not all zeros
    if key.iter().all(|&b| b == 0) {
        return Err(X25519Error::ValidationError("Generated key is all zeros".to_string()));
    }
    
    // Apply X25519 key clamping
    key[0] &= 0xf8;  // Clear the 3 least significant bits
    key[31] &= 0x7f; // Clear the most significant bit
    key[31] |= 0x40; // Set the second most significant bit
    
    Ok(key)
}

#[cfg(test)]
mod tests {
    use super::*;
    use rand::rngs::OsRng;


    #[test]
    fn test_x25519() {
        let scalar = [
            0xa5u8, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46,
            0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44,
            0xba, 0x44, 0x9a, 0xc4,
        ];
        let u_coordinate = [
            0xe6u8, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1,
            0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6,
            0xd0, 0xab, 0x1c, 0x4c,
        ];
        let result = x25519(scalar, u_coordinate).unwrap();

        assert_eq!(
            result,
            [
                0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d,
                0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55,
                0x77, 0xa2, 0x85, 0x52
            ]
        );

        let scalar = [
            0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d,
            0x6a, 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e,
            0x79, 0x18, 0xba, 0x0d,
        ];
        let u_coordinate = [
            0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38,
            0xae, 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49,
            0xc7, 0x15, 0xa4, 0x93,
        ];
        let result = x25519(scalar, u_coordinate).unwrap();

        assert_eq!(
            result,
            [
                0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8,
                0x73, 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64,
                0x7a, 0xac, 0x79, 0x57
            ]
        );
    }

    #[test]
    fn test_x25519_series() {
        let scalar = U_COORDINATE;
        let u_coordinate = U_COORDINATE;
        let result = x25519(scalar, u_coordinate).unwrap();

        assert_eq!(
            result,
            [
                0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7,
                0x27, 0x9f, 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, 0x3c, 0x60, 0xe8, 0x03,
                0x11, 0xae, 0x30, 0x79
            ]
        );
    }

    #[test]
    fn test_generate_private_key() {
        let mut rng = OsRng;
        let private_key = generate_private_key(&mut rng).unwrap();
        
        // Check that the key is not all zeros
        assert!(!private_key.iter().all(|&b| b == 0));
        
        // Check that the key has been properly clamped
        assert_eq!(private_key[0] & 0x07, 0); // Bottom 3 bits should be 0
        assert_eq!(private_key[31] & 0x80, 0); // Top bit should be 0
        assert_eq!(private_key[31] & 0x40, 0x40); // Second top bit should be 1
    }

    #[test]
    fn test_invalid_input() {
        let zero_input = [0u8; CURVE25519_BYTE_LEN];
        let valid_input = U_COORDINATE;
        
        // Test with zero scalar
        assert!(matches!(x25519(zero_input, valid_input), Err(X25519Error::InvalidInput)));
        
        // Test with zero u-coordinate
        assert!(matches!(x25519(valid_input, zero_input), Err(X25519Error::InvalidInput)));
        
        // Test with both zero
        assert!(matches!(x25519(zero_input, zero_input), Err(X25519Error::InvalidInput)));
    }

    #[test]
    fn test_key_exchange() {
        let mut rng = OsRng;
        
        // Generate private keys for Alice and Bob
        let alice_private = generate_private_key(&mut rng).unwrap();
        let bob_private = generate_private_key(&mut rng).unwrap();
        
        // Compute public keys
        let alice_public = x25519(alice_private, U_COORDINATE).unwrap();
        let bob_public = x25519(bob_private, U_COORDINATE).unwrap();
        
        // Perform key exchange
        let alice_shared = x25519(alice_private, bob_public).unwrap();
        let bob_shared = x25519(bob_private, alice_public).unwrap();
        
        // Both parties should have the same shared secret
        assert_eq!(alice_shared, bob_shared);
    }
}