apple_cryptokit/
lib.rs

1//! # Apple CryptoKit for Rust
2//!
3//! This crate provides Rust bindings to Apple's CryptoKit, enabling the use of optimized
4//! cryptographic algorithms on macOS, iOS, and other Apple platforms.
5//!
6//! ## Features
7//!
8//! - **Hash Algorithms**: SHA256, SHA384, SHA512, etc.
9//! - **Message Authentication Codes**: HMAC-SHA256, HMAC-SHA384, etc.
10//! - **Symmetric Encryption**: AES-GCM, ChaCha20-Poly1305
11//! - **Asymmetric Encryption**: Elliptic Curve Cryptography (P256, P384, P521, Curve25519)
12//! - **Quantum-Safe Algorithms**: ML-KEM, X-Wing, ML-DSA (quantum-resistant)
13//! - **Key Derivation**: HKDF
14//! - **Key Management**: Symmetric key generation and management
15//!
16//! ## Examples
17//!
18//! ### Traditional Cryptography
19//!
20//! ```rust,no_run
21//! use apple_cryptokit::hashing::{sha256_hash, SHA256, HashFunction};
22//! use apple_cryptokit::symmetric::aes::{aes_gcm_encrypt, aes_gcm_decrypt};
23//!
24//! # fn main() -> apple_cryptokit::Result<()> {
25//! // Hash computation
26//! let data = b"Hello, World!";
27//! let hash = sha256_hash(data);
28//! // Or use the trait
29//! let hash = SHA256::hash(data);
30//!
31//! // Symmetric encryption
32//! let key = b"0123456789abcdef0123456789abcdef"; // 32-byte key
33//! let nonce = b"cdef01234567"; // 12-byte nonce
34//! let plaintext = b"Secret message";
35//!
36//! let ciphertext = aes_gcm_encrypt(key, nonce, plaintext)?;
37//! let decrypted = aes_gcm_decrypt(key, nonce, &ciphertext)?;
38//! # Ok(())
39//! # }
40//! ```
41//!
42//! ### Quantum-Safe Cryptography
43//!
44//! ```rust,no_run
45//! use apple_cryptokit::quantum::{MLKem768, XWingMLKem768X25519, MLDsa65};
46//! use apple_cryptokit::quantum::{KEMPrivateKey, KEMPublicKey, KeyEncapsulationMechanism};
47//! use apple_cryptokit::quantum::{SignaturePrivateKey, SignaturePublicKey, DigitalSignatureAlgorithm};
48//!
49//! # fn main() -> apple_cryptokit::Result<()> {
50//! // ML-KEM768 key encapsulation
51//! let private_key = MLKem768::generate_private_key()?;
52//! let public_key = private_key.public_key();
53//!
54//! let (ciphertext, shared_secret) = public_key.encapsulate()?;
55//! let decapsulated_secret = private_key.decapsulate(&ciphertext)?;
56//! assert_eq!(shared_secret, decapsulated_secret);
57//!
58//! // X-Wing hybrid KEM (combining ML-KEM768 and X25519)
59//! let xwing_private = XWingMLKem768X25519::generate_private_key()?;
60//! let xwing_public = xwing_private.public_key();
61//!
62//! let (xwing_ciphertext, xwing_secret) = xwing_public.encapsulate()?;
63//! let xwing_decapsulated = xwing_private.decapsulate(&xwing_ciphertext)?;
64//! assert_eq!(xwing_secret, xwing_decapsulated);
65//!
66//! // ML-DSA65 digital signature
67//! let sign_private = MLDsa65::generate_private_key()?;
68//! let sign_public = sign_private.public_key();
69//!
70//! let message = b"Hello, post-quantum world!";
71//! let signature = sign_private.sign(message)?;
72//! let is_valid = sign_public.verify(message, &signature)?;
73//! assert!(is_valid);
74//! # Ok(())
75//! # }
76//! ```
77
78pub mod asymmetric;
79pub mod authentication;
80pub mod error;
81pub mod hashing;
82pub mod key_derivation;
83pub mod keys;
84pub mod quantum;
85pub mod symmetric;
86
87// Re-export commonly used types and functions for convenience
88pub use error::{CryptoKitError, Result};
89
90// Re-export hash-related items
91pub use hashing::{
92    sha1_hash, sha256_hash, sha384_hash, sha512_hash, HashAlgorithm, HashBuilder, HashFunction,
93    Sha256, Sha384, Sha512, SHA1, SHA256, SHA384, SHA512,
94};
95
96// Re-export HMAC-related items
97pub use authentication::{HMAC, hmac_sha1, hmac_sha256, hmac_sha384, hmac_sha512};
98
99// Re-export symmetric encryption items
100pub use symmetric::aes::{
101    aes_gcm_decrypt, aes_gcm_decrypt_with_aad, aes_gcm_encrypt, aes_gcm_encrypt_with_aad,
102    AESGCMNonce, AESKey, AESKeySize, AesGcm,
103};
104pub use symmetric::chacha::{
105    chacha20poly1305_decrypt, chacha20poly1305_decrypt_with_aad, chacha20poly1305_encrypt,
106    chacha20poly1305_encrypt_with_aad, ChaChaKey, ChaChaPoly, ChaChaPolyNonce,
107};
108pub use symmetric::{AuthenticatedCipher, Cipher};
109
110// Re-export key derivation items
111pub use key_derivation::{
112    hkdf_sha256_derive, hkdf_sha384_derive, hkdf_sha512_derive, KeyDerivationFunction,
113};
114
115// Re-export key management items
116pub use keys::{SymmetricKey, SymmetricKeySize};
117
118// Re-export quantum-safe algorithm items
119pub use quantum::{
120    DigitalSignatureAlgorithm, KEMPrivateKey, KEMPublicKey, KeyEncapsulationMechanism, MLDsa65,
121    MLDsa65PrivateKey, MLDsa65PublicKey, MLDsa87, MLDsa87PrivateKey, MLDsa87PublicKey, MLKem1024,
122    MLKem1024PrivateKey, MLKem1024PublicKey, MLKem768, MLKem768PrivateKey, MLKem768PublicKey,
123    QuantumSafe, SignaturePrivateKey, SignaturePublicKey, XWingMLKem768X25519,
124    XWingMLKem768X25519PrivateKey, XWingMLKem768X25519PublicKey,
125};
126
127// @deprecated Use functions from the `hashing` module instead
128extern "C" {
129    #[link_name = "md5_hash"]
130    fn swift_md5_hash(data: *const u8, length: i32, out_hash: *mut u8);
131}
132
133// @deprecated Use functions from the `hashing` module instead
134// MD5 hash computation (insecure, for compatibility only)
135pub fn md5_hash(data: &[u8]) -> Vec<u8> {
136    unsafe {
137        let mut output_hash = [0u8; 16];
138        swift_md5_hash(data.as_ptr(), data.len() as i32, output_hash.as_mut_ptr());
139        output_hash.to_vec()
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn test_sha256_basic() {
149        let input = b"abc";
150        let expected: [u8; 32] = [
151            0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae,
152            0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61,
153            0xf2, 0x00, 0x15, 0xad,
154        ];
155        let hash = sha256_hash(input);
156        assert_eq!(hash, expected);
157    }
158
159    #[test]
160    fn test_sha256_trait() {
161        let input = b"abc";
162        let expected: [u8; 32] = [
163            0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae,
164            0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61,
165            0xf2, 0x00, 0x15, 0xad,
166        ];
167        let hash = SHA256::hash(input);
168        assert_eq!(hash, expected);
169    }
170
171    #[test]
172    fn test_sha256_streaming() {
173        let mut hasher = Sha256::new();
174        hasher.update(b"a");
175        hasher.update(b"b");
176        hasher.update(b"c");
177        let hash = hasher.finalize();
178
179        let expected: [u8; 32] = [
180            0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae,
181            0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61,
182            0xf2, 0x00, 0x15, 0xad,
183        ];
184        assert_eq!(hash, expected);
185    }
186
187    #[test]
188    fn test_aes_gcm_roundtrip() {
189        let key = b"0123456789abcdef0123456789abcdef"; // 32-byte key
190        let nonce = b"cdef01234567"; // 12-byte nonce
191        let plaintext = b"Hello, World!";
192
193        // Test encryption and decryption round-trip
194        let ciphertext = aes_gcm_encrypt(key, nonce, plaintext).unwrap();
195        assert!(ciphertext.len() > plaintext.len()); // Should be longer due to tag
196
197        let decrypted = aes_gcm_decrypt(key, nonce, &ciphertext).unwrap();
198        assert_eq!(decrypted, plaintext);
199    }
200
201    #[test]
202    fn test_chacha20poly1305_roundtrip() {
203        let key = b"0123456789abcdef0123456789abcdef"; // 32-byte key
204        let nonce = b"cdef01234567"; // 12-byte nonce
205        let plaintext = b"Hello, ChaCha20Poly1305!";
206
207        // Test encryption and decryption round-trip
208        let ciphertext = chacha20poly1305_encrypt(key, nonce, plaintext).unwrap();
209        assert!(ciphertext.len() > plaintext.len()); // Should be longer due to tag
210
211        let decrypted = chacha20poly1305_decrypt(key, nonce, &ciphertext).unwrap();
212        assert_eq!(decrypted, plaintext);
213    }
214
215    #[test]
216    fn test_hmac_sha256() {
217        let key = b"key";
218        let message = b"message";
219        let result = hmac_sha256(key, message).unwrap();
220        assert_eq!(result.len(), 32); // SHA256 outputs 32 bytes
221
222        // Test known vector
223        let key = b"Jefe";
224        let message = b"what do ya want for nothing?";
225        let result = hmac_sha256(key, message).unwrap();
226        let expected: [u8; 32] = [
227            0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95,
228            0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, 0x9d, 0xec, 0x58, 0xb9,
229            0x64, 0xec, 0x38, 0x43,
230        ];
231        assert_eq!(result, expected);
232    }
233
234    #[test]
235    fn test_md5_compatibility() {
236        let input = b"hello";
237        let hash = md5_hash(input);
238        // Expected MD5 hash for "hello"
239        let expected: [u8; 16] = [
240            0x5d, 0x41, 0x40, 0x2a, 0xbc, 0x4b, 0x2a, 0x76, 0xb9, 0x71, 0x9d, 0x91, 0x10, 0x17,
241            0xc5, 0x92,
242        ];
243        assert_eq!(hash, expected);
244    }
245}