chie_crypto/
webcrypto.rs

1//! WebCrypto API Compatibility Layer
2//!
3//! This module provides compatibility with the W3C WebCrypto API standard,
4//! enabling interoperability between Rust cryptographic operations and
5//! browser-based WebCrypto operations.
6//!
7//! # Examples
8//!
9//! ```
10//! use chie_crypto::webcrypto::{WebCryptoKey, Algorithm, KeyUsage};
11//! use chie_crypto::signing::KeyPair;
12//!
13//! // Create a WebCrypto-compatible key
14//! let keypair = KeyPair::generate();
15//! let web_key = WebCryptoKey::from_ed25519_keypair(&keypair, &[KeyUsage::Sign]);
16//!
17//! // Export to JWK (WebCrypto standard format)
18//! let jwk = web_key.to_jwk().unwrap();
19//! ```
20
21use crate::key_formats::JwkKey;
22use crate::signing::{KeyPair, PublicKey};
23use serde::{Deserialize, Serialize};
24use thiserror::Error;
25
26/// WebCrypto API errors
27#[derive(Debug, Error, Clone, PartialEq, Eq)]
28pub enum WebCryptoError {
29    /// Unsupported algorithm
30    #[error("Unsupported algorithm: {0}")]
31    UnsupportedAlgorithm(String),
32
33    /// Invalid key usage
34    #[error("Invalid key usage: {0}")]
35    InvalidKeyUsage(String),
36
37    /// Key format error
38    #[error("Key format error: {0}")]
39    KeyFormatError(String),
40
41    /// Operation not permitted
42    #[error("Operation not permitted with current key usages")]
43    OperationNotPermitted,
44}
45
46/// Result type for WebCrypto operations
47pub type WebCryptoResult<T> = Result<T, WebCryptoError>;
48
49/// WebCrypto algorithm identifiers
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
51#[serde(tag = "name")]
52#[allow(non_snake_case)]
53pub enum Algorithm {
54    /// EdDSA (Ed25519) - Digital signatures
55    #[serde(rename = "EdDSA")]
56    EdDSA { namedCurve: String },
57
58    /// ECDH (X25519) - Key agreement
59    #[serde(rename = "ECDH")]
60    ECDH { namedCurve: String },
61
62    /// AES-GCM - Authenticated encryption
63    #[serde(rename = "AES-GCM")]
64    AesGcm { length: u32 },
65
66    /// HMAC - Message authentication
67    #[serde(rename = "HMAC")]
68    Hmac { hash: String },
69
70    /// HKDF - Key derivation
71    #[serde(rename = "HKDF")]
72    Hkdf { hash: String },
73
74    /// PBKDF2 - Password-based key derivation
75    #[serde(rename = "PBKDF2")]
76    Pbkdf2 {
77        hash: String,
78        iterations: u32,
79        salt: Vec<u8>,
80    },
81}
82
83impl Algorithm {
84    /// Create EdDSA algorithm with Ed25519 curve
85    pub fn ed_dsa() -> Self {
86        Self::EdDSA {
87            namedCurve: "Ed25519".to_string(),
88        }
89    }
90
91    /// Create ECDH algorithm with X25519 curve
92    pub fn ecdh() -> Self {
93        Self::ECDH {
94            namedCurve: "X25519".to_string(),
95        }
96    }
97
98    /// Create AES-GCM algorithm with specified key length
99    pub fn aes_gcm(length: u32) -> Self {
100        Self::AesGcm { length }
101    }
102
103    /// Create HMAC algorithm with specified hash
104    pub fn hmac(hash: impl Into<String>) -> Self {
105        Self::Hmac { hash: hash.into() }
106    }
107
108    /// Create HKDF algorithm with specified hash
109    pub fn hkdf(hash: impl Into<String>) -> Self {
110        Self::Hkdf { hash: hash.into() }
111    }
112
113    /// Create PBKDF2 algorithm
114    pub fn pbkdf2(hash: impl Into<String>, iterations: u32, salt: Vec<u8>) -> Self {
115        Self::Pbkdf2 {
116            hash: hash.into(),
117            iterations,
118            salt,
119        }
120    }
121}
122
123/// WebCrypto key usage flags
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
125#[serde(rename_all = "camelCase")]
126pub enum KeyUsage {
127    /// Key can be used for encryption
128    Encrypt,
129    /// Key can be used for decryption
130    Decrypt,
131    /// Key can be used for signing
132    Sign,
133    /// Key can be used for verification
134    Verify,
135    /// Key can be used for key derivation
136    DeriveKey,
137    /// Key can be used to derive bits
138    DeriveBits,
139    /// Key can be wrapped
140    WrapKey,
141    /// Key can be unwrapped
142    UnwrapKey,
143}
144
145impl KeyUsage {
146    /// Check if usage is a signing operation
147    pub fn is_signing(&self) -> bool {
148        matches!(self, Self::Sign | Self::Verify)
149    }
150
151    /// Check if usage is an encryption operation
152    pub fn is_encryption(&self) -> bool {
153        matches!(self, Self::Encrypt | Self::Decrypt)
154    }
155
156    /// Check if usage is a key derivation operation
157    pub fn is_derivation(&self) -> bool {
158        matches!(self, Self::DeriveKey | Self::DeriveBits)
159    }
160}
161
162/// WebCrypto-compatible key
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct WebCryptoKey {
165    /// Algorithm
166    pub algorithm: Algorithm,
167    /// Key type (public, private, secret)
168    #[serde(rename = "type")]
169    pub key_type: KeyType,
170    /// Extractable flag
171    pub extractable: bool,
172    /// Key usages
173    pub usages: Vec<KeyUsage>,
174    /// Internal key data (not exposed in serialization)
175    #[serde(skip)]
176    key_data: Vec<u8>,
177}
178
179/// WebCrypto key type
180#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
181#[serde(rename_all = "lowercase")]
182pub enum KeyType {
183    /// Public key
184    Public,
185    /// Private key
186    Private,
187    /// Secret (symmetric) key
188    Secret,
189}
190
191impl WebCryptoKey {
192    /// Create WebCrypto key from Ed25519 keypair
193    pub fn from_ed25519_keypair(keypair: &KeyPair, usages: &[KeyUsage]) -> Self {
194        Self {
195            algorithm: Algorithm::ed_dsa(),
196            key_type: KeyType::Private,
197            extractable: true,
198            usages: usages.to_vec(),
199            key_data: keypair.secret_key().to_vec(),
200        }
201    }
202
203    /// Create WebCrypto key from Ed25519 public key
204    pub fn from_ed25519_public_key(public_key: &PublicKey, usages: &[KeyUsage]) -> Self {
205        Self {
206            algorithm: Algorithm::ed_dsa(),
207            key_type: KeyType::Public,
208            extractable: true,
209            usages: usages.to_vec(),
210            key_data: public_key.to_vec(),
211        }
212    }
213
214    /// Create WebCrypto key from symmetric key
215    pub fn from_symmetric_key(key: &[u8], algorithm: Algorithm, usages: &[KeyUsage]) -> Self {
216        Self {
217            algorithm,
218            key_type: KeyType::Secret,
219            extractable: true,
220            usages: usages.to_vec(),
221            key_data: key.to_vec(),
222        }
223    }
224
225    /// Set extractable flag
226    pub fn with_extractable(mut self, extractable: bool) -> Self {
227        self.extractable = extractable;
228        self
229    }
230
231    /// Check if key can be used for operation
232    pub fn can_use_for(&self, usage: KeyUsage) -> bool {
233        self.usages.contains(&usage)
234    }
235
236    /// Export key to JWK format (WebCrypto standard)
237    pub fn to_jwk(&self) -> WebCryptoResult<JwkKey> {
238        if !self.extractable {
239            return Err(WebCryptoError::OperationNotPermitted);
240        }
241
242        match (&self.algorithm, self.key_type) {
243            (Algorithm::EdDSA { .. }, KeyType::Private) => {
244                let mut secret = [0u8; 32];
245                secret.copy_from_slice(&self.key_data);
246                let keypair = KeyPair::from_secret_key(&secret).map_err(|_| {
247                    WebCryptoError::KeyFormatError("Invalid secret key".to_string())
248                })?;
249                Ok(JwkKey::from_ed25519_keypair(&keypair))
250            }
251            (Algorithm::EdDSA { .. }, KeyType::Public) => {
252                let mut public = [0u8; 32];
253                public.copy_from_slice(&self.key_data);
254                Ok(JwkKey::from_ed25519_public_key(&public))
255            }
256            _ => Err(WebCryptoError::UnsupportedAlgorithm(
257                "Only EdDSA keys can be exported to JWK".to_string(),
258            )),
259        }
260    }
261
262    /// Import key from JWK format
263    pub fn from_jwk(jwk: &JwkKey, usages: &[KeyUsage]) -> WebCryptoResult<Self> {
264        if jwk.kty != "OKP" {
265            return Err(WebCryptoError::UnsupportedAlgorithm(format!(
266                "Unsupported key type: {}",
267                jwk.kty
268            )));
269        }
270
271        let crv = jwk
272            .crv
273            .as_ref()
274            .ok_or_else(|| WebCryptoError::KeyFormatError("Missing curve parameter".to_string()))?;
275
276        if crv != "Ed25519" {
277            return Err(WebCryptoError::UnsupportedAlgorithm(format!(
278                "Unsupported curve: {}",
279                crv
280            )));
281        }
282
283        let algorithm = Algorithm::ed_dsa();
284
285        // Check if it's a private key
286        if jwk.d.is_some() {
287            let keypair = jwk.to_ed25519_keypair().map_err(|e| {
288                WebCryptoError::KeyFormatError(format!("Failed to import keypair: {}", e))
289            })?;
290
291            Ok(Self {
292                algorithm,
293                key_type: KeyType::Private,
294                extractable: true,
295                usages: usages.to_vec(),
296                key_data: keypair.secret_key().to_vec(),
297            })
298        } else {
299            let public_key = jwk.to_ed25519_public_key().map_err(|e| {
300                WebCryptoError::KeyFormatError(format!("Failed to import public key: {}", e))
301            })?;
302
303            Ok(Self {
304                algorithm,
305                key_type: KeyType::Public,
306                extractable: true,
307                usages: usages.to_vec(),
308                key_data: public_key.to_vec(),
309            })
310        }
311    }
312
313    /// Get key data (for internal use)
314    pub fn key_data(&self) -> &[u8] {
315        &self.key_data
316    }
317}
318
319/// WebCrypto key pair (for asymmetric algorithms)
320#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct WebCryptoKeyPair {
322    /// Public key
323    #[serde(rename = "publicKey")]
324    pub public_key: WebCryptoKey,
325    /// Private key
326    #[serde(rename = "privateKey")]
327    pub private_key: WebCryptoKey,
328}
329
330impl WebCryptoKeyPair {
331    /// Create from Ed25519 keypair
332    pub fn from_ed25519(keypair: &KeyPair, usages: &[KeyUsage]) -> Self {
333        let (sign_usages, verify_usages): (Vec<_>, Vec<_>) =
334            usages.iter().partition(|u| **u == KeyUsage::Sign);
335
336        Self {
337            public_key: WebCryptoKey::from_ed25519_public_key(
338                &keypair.public_key(),
339                &verify_usages,
340            ),
341            private_key: WebCryptoKey::from_ed25519_keypair(keypair, &sign_usages),
342        }
343    }
344}
345
346#[cfg(test)]
347mod tests {
348    use super::*;
349
350    #[test]
351    fn test_algorithm_creation() {
352        let algo = Algorithm::ed_dsa();
353        match algo {
354            Algorithm::EdDSA { namedCurve } => {
355                assert_eq!(namedCurve, "Ed25519");
356            }
357            _ => panic!("Wrong algorithm type"),
358        }
359    }
360
361    #[test]
362    fn test_key_usage_checks() {
363        assert!(KeyUsage::Sign.is_signing());
364        assert!(KeyUsage::Verify.is_signing());
365        assert!(KeyUsage::Encrypt.is_encryption());
366        assert!(KeyUsage::Decrypt.is_encryption());
367        assert!(KeyUsage::DeriveKey.is_derivation());
368        assert!(KeyUsage::DeriveBits.is_derivation());
369    }
370
371    #[test]
372    fn test_webcrypto_key_from_ed25519() {
373        let keypair = KeyPair::generate();
374        let usages = vec![KeyUsage::Sign];
375
376        let web_key = WebCryptoKey::from_ed25519_keypair(&keypair, &usages);
377
378        assert_eq!(web_key.key_type, KeyType::Private);
379        assert!(web_key.extractable);
380        assert_eq!(web_key.usages, usages);
381        assert!(web_key.can_use_for(KeyUsage::Sign));
382        assert!(!web_key.can_use_for(KeyUsage::Verify));
383    }
384
385    #[test]
386    fn test_webcrypto_public_key() {
387        let keypair = KeyPair::generate();
388        let public_key = keypair.public_key();
389        let usages = vec![KeyUsage::Verify];
390
391        let web_key = WebCryptoKey::from_ed25519_public_key(&public_key, &usages);
392
393        assert_eq!(web_key.key_type, KeyType::Public);
394        assert!(web_key.can_use_for(KeyUsage::Verify));
395    }
396
397    #[test]
398    fn test_jwk_export() {
399        let keypair = KeyPair::generate();
400        let usages = vec![KeyUsage::Sign];
401
402        let web_key = WebCryptoKey::from_ed25519_keypair(&keypair, &usages);
403        let jwk = web_key.to_jwk().unwrap();
404
405        assert_eq!(jwk.kty, "OKP");
406        assert_eq!(jwk.crv, Some("Ed25519".to_string()));
407        assert!(jwk.d.is_some()); // Private key
408    }
409
410    #[test]
411    fn test_jwk_import_private() {
412        let keypair = KeyPair::generate();
413        let jwk = JwkKey::from_ed25519_keypair(&keypair);
414        let usages = vec![KeyUsage::Sign];
415
416        let web_key = WebCryptoKey::from_jwk(&jwk, &usages).unwrap();
417
418        assert_eq!(web_key.key_type, KeyType::Private);
419        assert_eq!(web_key.usages, usages);
420    }
421
422    #[test]
423    fn test_jwk_import_public() {
424        let keypair = KeyPair::generate();
425        let public_key = keypair.public_key();
426        let jwk = JwkKey::from_ed25519_public_key(&public_key);
427        let usages = vec![KeyUsage::Verify];
428
429        let web_key = WebCryptoKey::from_jwk(&jwk, &usages).unwrap();
430
431        assert_eq!(web_key.key_type, KeyType::Public);
432        assert_eq!(web_key.usages, usages);
433    }
434
435    #[test]
436    fn test_extractable_flag() {
437        let keypair = KeyPair::generate();
438        let web_key =
439            WebCryptoKey::from_ed25519_keypair(&keypair, &[KeyUsage::Sign]).with_extractable(false);
440
441        assert!(!web_key.extractable);
442        assert!(web_key.to_jwk().is_err());
443    }
444
445    #[test]
446    fn test_keypair_creation() {
447        let keypair = KeyPair::generate();
448        let usages = vec![KeyUsage::Sign, KeyUsage::Verify];
449
450        let web_keypair = WebCryptoKeyPair::from_ed25519(&keypair, &usages);
451
452        assert_eq!(web_keypair.public_key.key_type, KeyType::Public);
453        assert_eq!(web_keypair.private_key.key_type, KeyType::Private);
454    }
455
456    #[test]
457    fn test_algorithm_serialization() {
458        let algo = Algorithm::ed_dsa();
459        let json = serde_json::to_string(&algo).unwrap();
460        assert!(json.contains("EdDSA"));
461        assert!(json.contains("Ed25519"));
462
463        let deserialized: Algorithm = serde_json::from_str(&json).unwrap();
464        assert_eq!(deserialized, algo);
465    }
466
467    #[test]
468    fn test_key_usage_serialization() {
469        let usage = KeyUsage::Sign;
470        let json = serde_json::to_string(&usage).unwrap();
471        assert_eq!(json, "\"sign\"");
472
473        let deserialized: KeyUsage = serde_json::from_str(&json).unwrap();
474        assert_eq!(deserialized, usage);
475    }
476
477    #[test]
478    fn test_webcrypto_key_serialization() {
479        let keypair = KeyPair::generate();
480        let web_key = WebCryptoKey::from_ed25519_keypair(&keypair, &[KeyUsage::Sign]);
481
482        // Use JSON serialization (more appropriate for WebCrypto)
483        let serialized = serde_json::to_string(&web_key).unwrap();
484        let deserialized: WebCryptoKey = serde_json::from_str(&serialized).unwrap();
485
486        assert_eq!(deserialized.key_type, web_key.key_type);
487        assert_eq!(deserialized.extractable, web_key.extractable);
488        assert_eq!(deserialized.usages, web_key.usages);
489    }
490
491    #[test]
492    fn test_symmetric_key() {
493        let key = [0x42u8; 32];
494        let algo = Algorithm::aes_gcm(256);
495        let usages = vec![KeyUsage::Encrypt, KeyUsage::Decrypt];
496
497        let web_key = WebCryptoKey::from_symmetric_key(&key, algo.clone(), &usages);
498
499        assert_eq!(web_key.key_type, KeyType::Secret);
500        assert_eq!(web_key.algorithm, algo);
501        assert!(web_key.can_use_for(KeyUsage::Encrypt));
502        assert!(web_key.can_use_for(KeyUsage::Decrypt));
503    }
504
505    #[test]
506    fn test_hmac_algorithm() {
507        let algo = Algorithm::hmac("SHA-256");
508        match algo {
509            Algorithm::Hmac { hash } => {
510                assert_eq!(hash, "SHA-256");
511            }
512            _ => panic!("Wrong algorithm type"),
513        }
514    }
515
516    #[test]
517    fn test_hkdf_algorithm() {
518        let algo = Algorithm::hkdf("SHA-256");
519        match algo {
520            Algorithm::Hkdf { hash } => {
521                assert_eq!(hash, "SHA-256");
522            }
523            _ => panic!("Wrong algorithm type"),
524        }
525    }
526
527    #[test]
528    fn test_pbkdf2_algorithm() {
529        let salt = vec![1, 2, 3, 4];
530        let algo = Algorithm::pbkdf2("SHA-256", 100000, salt.clone());
531        match algo {
532            Algorithm::Pbkdf2 {
533                hash,
534                iterations,
535                salt: s,
536            } => {
537                assert_eq!(hash, "SHA-256");
538                assert_eq!(iterations, 100000);
539                assert_eq!(s, salt);
540            }
541            _ => panic!("Wrong algorithm type"),
542        }
543    }
544}