1use crate::key_formats::JwkKey;
22use crate::signing::{KeyPair, PublicKey};
23use serde::{Deserialize, Serialize};
24use thiserror::Error;
25
26#[derive(Debug, Error, Clone, PartialEq, Eq)]
28pub enum WebCryptoError {
29 #[error("Unsupported algorithm: {0}")]
31 UnsupportedAlgorithm(String),
32
33 #[error("Invalid key usage: {0}")]
35 InvalidKeyUsage(String),
36
37 #[error("Key format error: {0}")]
39 KeyFormatError(String),
40
41 #[error("Operation not permitted with current key usages")]
43 OperationNotPermitted,
44}
45
46pub type WebCryptoResult<T> = Result<T, WebCryptoError>;
48
49#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
51#[serde(tag = "name")]
52#[allow(non_snake_case)]
53pub enum Algorithm {
54 #[serde(rename = "EdDSA")]
56 EdDSA { namedCurve: String },
57
58 #[serde(rename = "ECDH")]
60 ECDH { namedCurve: String },
61
62 #[serde(rename = "AES-GCM")]
64 AesGcm { length: u32 },
65
66 #[serde(rename = "HMAC")]
68 Hmac { hash: String },
69
70 #[serde(rename = "HKDF")]
72 Hkdf { hash: String },
73
74 #[serde(rename = "PBKDF2")]
76 Pbkdf2 {
77 hash: String,
78 iterations: u32,
79 salt: Vec<u8>,
80 },
81}
82
83impl Algorithm {
84 pub fn ed_dsa() -> Self {
86 Self::EdDSA {
87 namedCurve: "Ed25519".to_string(),
88 }
89 }
90
91 pub fn ecdh() -> Self {
93 Self::ECDH {
94 namedCurve: "X25519".to_string(),
95 }
96 }
97
98 pub fn aes_gcm(length: u32) -> Self {
100 Self::AesGcm { length }
101 }
102
103 pub fn hmac(hash: impl Into<String>) -> Self {
105 Self::Hmac { hash: hash.into() }
106 }
107
108 pub fn hkdf(hash: impl Into<String>) -> Self {
110 Self::Hkdf { hash: hash.into() }
111 }
112
113 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
125#[serde(rename_all = "camelCase")]
126pub enum KeyUsage {
127 Encrypt,
129 Decrypt,
131 Sign,
133 Verify,
135 DeriveKey,
137 DeriveBits,
139 WrapKey,
141 UnwrapKey,
143}
144
145impl KeyUsage {
146 pub fn is_signing(&self) -> bool {
148 matches!(self, Self::Sign | Self::Verify)
149 }
150
151 pub fn is_encryption(&self) -> bool {
153 matches!(self, Self::Encrypt | Self::Decrypt)
154 }
155
156 pub fn is_derivation(&self) -> bool {
158 matches!(self, Self::DeriveKey | Self::DeriveBits)
159 }
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct WebCryptoKey {
165 pub algorithm: Algorithm,
167 #[serde(rename = "type")]
169 pub key_type: KeyType,
170 pub extractable: bool,
172 pub usages: Vec<KeyUsage>,
174 #[serde(skip)]
176 key_data: Vec<u8>,
177}
178
179#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
181#[serde(rename_all = "lowercase")]
182pub enum KeyType {
183 Public,
185 Private,
187 Secret,
189}
190
191impl WebCryptoKey {
192 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 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 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 pub fn with_extractable(mut self, extractable: bool) -> Self {
227 self.extractable = extractable;
228 self
229 }
230
231 pub fn can_use_for(&self, usage: KeyUsage) -> bool {
233 self.usages.contains(&usage)
234 }
235
236 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 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 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 pub fn key_data(&self) -> &[u8] {
315 &self.key_data
316 }
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct WebCryptoKeyPair {
322 #[serde(rename = "publicKey")]
324 pub public_key: WebCryptoKey,
325 #[serde(rename = "privateKey")]
327 pub private_key: WebCryptoKey,
328}
329
330impl WebCryptoKeyPair {
331 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()); }
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 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}