cas-lib 0.2.89

A function wrapper layer for RustCrypto and Dalek-Cryptography. Intended to be used in FFI situations with a global heap deallactor at the top level project.
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
# Examples

This document provides runnable examples for each cryptographic module in `cas-lib`.
Most examples follow the same pattern described in
[issue #7](https://github.com/Cryptographic-API-Services/cas-lib/issues/7): read a file
from disk, get its bytes, and perform the cryptographic operation on them.

To try any of these:

```bash
cargo new cas-demo
cd cas-demo
cargo add cas-lib
# paste an example into src/main.rs, then:
cargo run
```

> **Note on "Digital Signature" and "ecdsa".** These two items appeared on the
> original checklist in issue #7 but the standalone Digital Signature
> implementation was removed from the library (it did little beyond what the
> hashers already provide), and a dedicated ECDSA module was never shipped.
> Use [Ed25519 signatures]#signatures-ed25519 or
> [RSA sign/verify]#asymmetric-rsa instead.

## Table of Contents

- [Password Hashers]#password-hashers
- [Symmetric]#symmetric
- [Hashers]#hashers
- [Asymmetric (RSA)]#asymmetric-rsa
- [Signatures (Ed25519)]#signatures-ed25519
- [Key Exchange (X25519)]#key-exchange-x25519
- [Message Authentication (HMAC)]#message-authentication-hmac
- [Hybrid (HPKE)]#hybrid-hpke
- [Sponges (ASCON-AEAD)]#sponges-ascon-aead
- [Compression (Zstandard)]#compression-zstandard

## Password Hashers
### Argon2
```rust
use cas_lib::password_hashers::{argon2::CASArgon, cas_password_hasher::CASPasswordHasher};

fn main() {
let password_to_hash = "HashThisBadPassword".to_string();
let hash = CASArgon::hash_password(password_to_hash);
println!("{}", hash)
}
```
### BCrypt
```rust
use cas_lib::password_hashers::{bcrypt::CASBCrypt, cas_password_hasher::CASPasswordHasher};

let password_to_hash = "HashThisBadPassword".to_string();
let hash = CASBCrypt::hash_password(password_to_hash);
println!("{}", hash);
```
### SCrypt
```rust
use cas_lib::password_hashers::{bcrypt::CASScrypt, cas_password_hasher::CASPasswordHasher};

let password_to_hash = "HashThisBadPassword".to_string();
let hash = CASScrypt::hash_password(password_to_hash);
println!("{}", hash);
```


## Symmetric

The symmetric module exposes AES-128-GCM, AES-256-GCM, and ChaCha20-Poly1305.
Nonces and keys are generated for you via `OsRng` — you never supply them
yourself. `encrypt_plaintext` returns the ciphertext with the authentication tag
appended; pass that same output back to `decrypt_ciphertext`. Each operation
returns a `Result`, so handle the error (here with `.unwrap()`).

### AES-256 GCM Mode
```rust
use std::{fs::File, io::Write, path::Path};

use cas_lib::symmetric::{aes::CASAES256, cas_symmetric_encryption::CASAES256Encryption};

fn main() {
    // Read the original file from disk.
    let path = Path::new("MikeMulchrone_Resume2024.docx");
    let file_bytes: Vec<u8> = std::fs::read(path).unwrap();

    // Generate a fresh key and nonce, then encrypt and write the ciphertext to disk.
    let aes_nonce = <CASAES256 as CASAES256Encryption>::generate_nonce();
    let aes_key = <CASAES256 as CASAES256Encryption>::generate_key();
    let encrypted_bytes = <CASAES256 as CASAES256Encryption>::encrypt_plaintext(
        aes_key.clone(),
        aes_nonce.clone(),
        file_bytes.clone(),
    )
    .unwrap();
    let mut file = File::create("encrypted.docx").unwrap();
    file.write_all(&encrypted_bytes).unwrap();

    // Decrypt with the same key and nonce; the result matches the original file.
    let decrypted_bytes =
        <CASAES256 as CASAES256Encryption>::decrypt_ciphertext(aes_key, aes_nonce, encrypted_bytes)
            .unwrap();
    let mut file = File::create("decrypted.docx").unwrap();
    file.write_all(&decrypted_bytes).unwrap();

    assert_eq!(file_bytes, decrypted_bytes);
}
```

### AES-128 GCM Mode
```rust
use std::{fs::File, io::Write, path::Path};

use cas_lib::symmetric::{aes::CASAES128, cas_symmetric_encryption::CASAES128Encryption};

fn main() {
    let path = Path::new("MikeMulchrone_Resume2024.docx");
    let file_bytes: Vec<u8> = std::fs::read(path).unwrap();

    let aes_nonce = <CASAES128 as CASAES128Encryption>::generate_nonce();
    let aes_key = <CASAES128 as CASAES128Encryption>::generate_key();
    let encrypted_bytes = <CASAES128 as CASAES128Encryption>::encrypt_plaintext(
        aes_key.clone(),
        aes_nonce.clone(),
        file_bytes.clone(),
    )
    .unwrap();
    let mut file = File::create("encrypted.docx").unwrap();
    file.write_all(&encrypted_bytes).unwrap();

    let decrypted_bytes =
        <CASAES128 as CASAES128Encryption>::decrypt_ciphertext(aes_key, aes_nonce, encrypted_bytes)
            .unwrap();
    let mut file = File::create("decrypted.docx").unwrap();
    file.write_all(&decrypted_bytes).unwrap();

    assert_eq!(file_bytes, decrypted_bytes);
}
```

### AES GCM Mode with AAD

Both AES types also implement the `CASAES256AadEncryption` / `CASAES128AadEncryption`
traits, which take additional authenticated data (AAD). The AAD is not encrypted, but
it is bound to the authentication tag: decryption only succeeds when the same AAD is
supplied, so any tampering with it is detected.

```rust
use cas_lib::symmetric::{
    aes::CASAES256,
    cas_symmetric_encryption::{CASAES256AadEncryption, CASAES256Encryption},
};

fn main() {
    let plaintext: Vec<u8> = std::fs::read("MikeMulchrone_Resume2024.docx").unwrap();
    let aad = b"file-id:1234".to_vec();

    let aes_nonce = <CASAES256 as CASAES256Encryption>::generate_nonce();
    let aes_key = <CASAES256 as CASAES256Encryption>::generate_key();
    let encrypted_bytes = <CASAES256 as CASAES256AadEncryption>::encrypt_plaintext_with_aad(
        aes_key.clone(),
        aes_nonce.clone(),
        plaintext.clone(),
        aad.clone(),
    )
    .unwrap();

    // Decryption requires the same AAD; a different AAD returns an error.
    let decrypted_bytes = <CASAES256 as CASAES256AadEncryption>::decrypt_ciphertext_with_aad(
        aes_key,
        aes_nonce,
        encrypted_bytes,
        aad,
    )
    .unwrap();

    assert_eq!(plaintext, decrypted_bytes);
}
```

### ChaCha20-Poly1305
```rust
use std::{fs::File, io::Write, path::Path};

use cas_lib::symmetric::{
    cas_symmetric_encryption::Chacha20Poly1305Encryption, chacha20poly1305::CASChacha20Poly1305,
};

fn main() {
    let path = Path::new("MikeMulchrone_Resume2024.docx");
    let file_bytes: Vec<u8> = std::fs::read(path).unwrap();

    let nonce = <CASChacha20Poly1305 as Chacha20Poly1305Encryption>::generate_nonce();
    let key = <CASChacha20Poly1305 as Chacha20Poly1305Encryption>::generate_key();
    let encrypted_bytes = <CASChacha20Poly1305 as Chacha20Poly1305Encryption>::encrypt_plaintext(
        key.clone(),
        nonce.clone(),
        file_bytes.clone(),
    )
    .unwrap();
    let mut file = File::create("encrypted.docx").unwrap();
    file.write_all(&encrypted_bytes).unwrap();

    let decrypted_bytes =
        <CASChacha20Poly1305 as Chacha20Poly1305Encryption>::decrypt_ciphertext(
            key,
            nonce,
            encrypted_bytes,
        )
        .unwrap();
    let mut file = File::create("decrypted.docx").unwrap();
    file.write_all(&decrypted_bytes).unwrap();

    assert_eq!(file_bytes, decrypted_bytes);
}
```

### AES Key Wrapping (AES-KW / AES-KWP)

The `CASAES256KeyWrap` / `CASAES128KeyWrap` traits implement AES Key Wrap
(RFC 3394 / NIST SP 800-38F KW) and AES Key Wrap with Padding (RFC 5649 / KWP).
They protect a data key under a key-encryption key (KEK), so the KEK can be
rotated by re-wrapping the data key instead of re-encrypting all the data.
`wrap_key` requires key data that is a multiple of 8 bytes and at least 16
bytes; `wrap_key_with_padding` accepts any length from 1 byte up.

```rust
use cas_lib::symmetric::{
    aes_kw::CASAES256KW, cas_symmetric_encryption::CASAES256KeyWrap,
};

fn main() {
    // The data key encrypts your data; the KEK only ever wraps the data key.
    let kek = <CASAES256KW as CASAES256KeyWrap>::generate_kek();
    let data_key = <CASAES256KW as CASAES256KeyWrap>::generate_kek();

    let wrapped =
        <CASAES256KW as CASAES256KeyWrap>::wrap_key(kek.clone(), data_key.clone()).unwrap();

    // Rotate the KEK: unwrap with the old KEK, re-wrap with a new one.
    // The data key never changes, so the data does not need re-encrypting.
    let new_kek = <CASAES256KW as CASAES256KeyWrap>::generate_kek();
    let recovered = <CASAES256KW as CASAES256KeyWrap>::unwrap_key(kek, wrapped).unwrap();
    let rewrapped =
        <CASAES256KW as CASAES256KeyWrap>::wrap_key(new_kek.clone(), recovered).unwrap();

    let unwrapped =
        <CASAES256KW as CASAES256KeyWrap>::unwrap_key(new_kek, rewrapped).unwrap();
    assert_eq!(unwrapped, data_key);
}
```


## Hashers

The hashers module provides SHA-2 / SHA-3 style digests (256- and 512-bit) via
the `CASHasher` trait. A digest is deterministic, so hashing the same file twice
produces equal output, while two different files produce different output. The
example below reads two files from disk and demonstrates both the `true`
(identical content) and `false` (different content) comparison cases described in
issue #7. `verify_256` / `verify_512` re-hash the data and compare it against a
previously produced digest.

```rust
use std::path::Path;

use cas_lib::hashers::{cas_hasher::CASHasher, sha::CASSHA};

fn main() {
    // Hash the first file.
    let path = Path::new("file_a.docx");
    let file_a: Vec<u8> = std::fs::read(path).unwrap();
    let hash_a = <CASSHA as CASHasher>::hash_256(file_a.clone());

    // Hashing the SAME file again yields the SAME digest -> true.
    let hash_a_again = <CASSHA as CASHasher>::hash_256(file_a.clone());
    println!("same file matches: {}", hash_a == hash_a_again); // true

    // Hashing a DIFFERENT file yields a DIFFERENT digest -> false.
    let path_b = Path::new("file_b.docx");
    let file_b: Vec<u8> = std::fs::read(path_b).unwrap();
    let hash_b = <CASSHA as CASHasher>::hash_256(file_b);
    println!("different file matches: {}", hash_a == hash_b); // false

    // verify_256 re-hashes the data and checks it against an existing digest.
    let verified = <CASSHA as CASHasher>::verify_256(hash_a, file_a);
    println!("verify_256: {}", verified); // true

    // 512-bit variants are also available:
    //   <CASSHA as CASHasher>::hash_512(data);
    //   <CASSHA as CASHasher>::verify_512(digest, data);
}
```


## Asymmetric (RSA)

The asymmetric module wraps RSA. Keys are PEM-encoded strings. Key sizes below
2048 bits are rejected. RSA here is used for signing and verifying a document's
bytes; every operation returns a `Result`.

```rust
use cas_lib::asymmetric::{
    cas_rsa::CASRSA,
    types::{CASRSAEncryption, RSAKeyPairResult},
};

fn main() {
    // Generate a 2048-bit RSA key pair (PEM-encoded strings).
    let key_pair: RSAKeyPairResult = CASRSA::generate_rsa_keys(2048).unwrap();

    // Read the document to sign.
    let document: Vec<u8> = std::fs::read("contract.pdf").unwrap();

    // Sign with the private key.
    let signature = CASRSA::sign(key_pair.private_key, document.clone()).unwrap();

    // Verify with the public key -> true when the document is unmodified.
    let is_valid = CASRSA::verify(key_pair.public_key, document, signature).unwrap();
    println!("signature valid: {}", is_valid); // true
}
```


## Signatures (Ed25519)

The signatures module provides Ed25519. `get_ed25519_key_pair` returns a key
pair; signing produces a 64-byte signature plus the 32-byte verification
(public) key. You can verify either with the full key pair or with just the
public key — the latter is what a remote verifier would typically hold.

```rust
use cas_lib::signatures::ed25519::{
    ed25519_sign_with_key_pair, ed25519_verify_with_public_key, get_ed25519_key_pair,
};

fn main() {
    // Generate a key pair.
    let key_pair = get_ed25519_key_pair();

    // Read the message/document to sign from disk.
    let message: Vec<u8> = std::fs::read("message.txt").unwrap();

    // Sign with the key pair.
    let signature = ed25519_sign_with_key_pair(key_pair.key_pair, message.clone()).unwrap();

    // A verifier holding only the public key can confirm the signature -> true.
    let is_valid =
        ed25519_verify_with_public_key(signature.public_key, signature.signature, message).unwrap();
    println!("signature valid: {}", is_valid); // true
}
```


## Key Exchange (X25519)

The key_exchange module implements X25519 Diffie-Hellman. Each party generates a
secret/public key pair; combining your secret key with the other party's public
key yields a shared secret that is identical on both sides. That shared secret
can then be turned into a symmetric key (see
[`key_from_x25519_shared_secret`](#symmetric) on the AES types).

```rust
use cas_lib::key_exchange::{
    cas_key_exchange::CASKeyExchange,
    x25519::{X25519, X25519SecretPublicKeyResult},
};

fn main() {
    // Each party generates a secret + public key pair.
    let alice: X25519SecretPublicKeyResult = X25519::generate_secret_and_public_key();
    let bob: X25519SecretPublicKeyResult = X25519::generate_secret_and_public_key();

    // Each side combines their own secret with the other's public key.
    let alice_shared = X25519::diffie_hellman(alice.secret_key, bob.public_key).unwrap();
    let bob_shared = X25519::diffie_hellman(bob.secret_key, alice.public_key).unwrap();

    // Both sides derive the SAME shared secret -> true.
    println!("shared secrets match: {}", alice_shared == bob_shared); // true
}
```


## Message Authentication (HMAC)

The message module provides HMAC. `sign` produces a tag over a message using a
shared key; `verify` recomputes the tag and confirms it matches. A correct
key + message + signature triple verifies as `true`. `HMAC` uses SHA2-256
(32-byte tag) and `HMACSha512` uses SHA2-512 (64-byte tag); both implement
the same `CASHMAC` trait.

```rust
use cas_lib::message::{cas_hmac::CASHMAC, hmac::{HMAC, HMACSha512}};

fn main() {
    // Shared secret key and the message bytes (read a file here in practice).
    let key: Vec<u8> = vec![1, 2, 3, 4, 5];
    let message: Vec<u8> = std::fs::read("message.txt").unwrap();

    // Produce an authentication tag (HMAC-SHA256).
    let signature = HMAC::sign(key.clone(), message.clone()).unwrap();

    // Verify the tag with the same key and message -> true.
    let is_valid = HMAC::verify(key.clone(), message.clone(), signature).unwrap();
    println!("hmac-sha256 valid: {}", is_valid); // true

    // Same API with HMAC-SHA512.
    let signature = HMACSha512::sign(key.clone(), message.clone()).unwrap();
    let is_valid = HMACSha512::verify(key, message, signature).unwrap();
    println!("hmac-sha512 valid: {}", is_valid); // true
}
```


## Hybrid (HPKE)

The hybrid module implements HPKE (Hybrid Public Key Encryption). The recipient
generates a key pair and an `info` string. A sender encrypts to the recipient's
public key, producing an encapsulated key, the ciphertext, and an authentication
tag. The recipient decrypts using their private key plus those three values.

```rust
use std::path::Path;

use cas_lib::hybrid::{cas_hybrid::CASHybrid, hpke::CASHPKE};

fn main() {
    // Read the file to encrypt.
    let file_bytes: Vec<u8> = std::fs::read(Path::new("secret.docx")).unwrap();

    // Recipient generates a key pair and info string.
    let (private_key, public_key, info_str) = CASHPKE::generate_key_pair();

    // Sender encrypts to the recipient's public key.
    let (encapped_key, ciphertext, tag) =
        CASHPKE::encrypt(file_bytes.clone(), public_key, info_str.clone()).unwrap();

    // Recipient decrypts with their private key + the encapsulated key + tag.
    let decrypted_bytes =
        CASHPKE::decrypt(ciphertext, private_key, encapped_key, tag, info_str).unwrap();

    assert_eq!(file_bytes, decrypted_bytes);
    println!("hpke round-trip succeeded");
}
```


## Sponges (ASCON-AEAD)

The sponges module provides ASCON-AEAD, a lightweight authenticated cipher. As
with the symmetric module, the key and nonce are generated for you, and
`encrypt` / `decrypt` round-trip the file bytes.

```rust
use std::{fs::File, io::Write, path::Path};

use cas_lib::sponges::{ascon_aead::AsconAead, cas_ascon_aead::CASAsconAead};

fn main() {
    let path = Path::new("secret.docx");
    let file_bytes: Vec<u8> = std::fs::read(path).unwrap();

    let nonce = <AsconAead as CASAsconAead>::generate_nonce();
    let key = <AsconAead as CASAsconAead>::generate_key();

    let encrypted_bytes =
        <AsconAead as CASAsconAead>::encrypt(key.clone(), nonce.clone(), file_bytes.clone())
            .unwrap();
    let mut file = File::create("encrypted.docx").unwrap();
    file.write_all(&encrypted_bytes).unwrap();

    let decrypted_bytes =
        <AsconAead as CASAsconAead>::decrypt(key, nonce, encrypted_bytes).unwrap();
    let mut file = File::create("decrypted.docx").unwrap();
    file.write_all(&decrypted_bytes).unwrap();

    assert_eq!(file_bytes, decrypted_bytes);
}
```


## Compression (Zstandard)

The compression module wraps Zstandard. `compress` takes the data and a
compression level (0-22; higher means smaller but slower); `decompress` restores
the original bytes exactly.

```rust
use cas_lib::compression::zstd::{compress, decompress};

fn main() {
    // Read a file from disk.
    let original: Vec<u8> = std::fs::read("large_log.txt").unwrap();

    // Compress at level 9.
    let compressed: Vec<u8> = compress(original.clone(), 9).unwrap();
    println!(
        "compressed {} bytes down to {} bytes",
        original.len(),
        compressed.len()
    );

    // Decompress back to the original bytes.
    let decompressed: Vec<u8> = decompress(compressed).unwrap();
    assert_eq!(original, decompressed);
}
```