|
4 | 4 | //! - thin-edge: docs/src/references/hsm-support.md
|
5 | 5 | //! - PKCS#11: https://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/os/pkcs11-base-v2.40-os.html
|
6 | 6 |
|
7 |
| -use asn1_rs::FromDer as _; |
8 |
| -pub use cryptoki::types::AuthPin; |
9 |
| - |
10 | 7 | use anyhow::Context;
|
| 8 | +use asn1_rs::FromDer as _; |
11 | 9 | use asn1_rs::ToDer;
|
12 | 10 | use camino::Utf8PathBuf;
|
13 | 11 | use cryptoki::context::CInitializeArgs;
|
@@ -35,6 +33,10 @@ use std::fmt::Debug;
|
35 | 33 | use std::sync::Arc;
|
36 | 34 | use std::sync::Mutex;
|
37 | 35 |
|
| 36 | +pub use cryptoki::types::AuthPin; |
| 37 | + |
| 38 | +mod uri; |
| 39 | + |
38 | 40 | // oIDs for curves defined here: https://datatracker.ietf.org/doc/html/rfc5480#section-2.1.1.1
|
39 | 41 | // other can be browsed here: https://oid-base.com/get/1.3.132.0.34
|
40 | 42 | const SECP256R1_OID: &str = "1.2.840.10045.3.1.7";
|
@@ -448,197 +450,3 @@ fn get_ec_mechanism(session: &Session, key: ObjectHandle) -> anyhow::Result<SigS
|
448 | 450 | _ => anyhow::bail!("Parsed oID({oid}) doesn't match any supported EC curve"),
|
449 | 451 | }
|
450 | 452 | }
|
451 |
| - |
452 |
| -pub mod uri { |
453 |
| - use std::borrow::Cow; |
454 |
| - use std::collections::HashMap; |
455 |
| - |
456 |
| - /// Attributes decoded from a PKCS #11 URL. |
457 |
| - /// |
458 |
| - /// Attributes only relevant to us shall be put into fields and the rest is in `other` hashmap. |
459 |
| - /// |
460 |
| - /// https://www.rfc-editor.org/rfc/rfc7512.html |
461 |
| - #[derive(Debug, Clone, Default)] |
462 |
| - pub struct Pkcs11Uri<'a> { |
463 |
| - pub token: Option<Cow<'a, str>>, |
464 |
| - pub serial: Option<Cow<'a, str>>, |
465 |
| - pub id: Option<Vec<u8>>, |
466 |
| - pub object: Option<Cow<'a, str>>, |
467 |
| - pub other: HashMap<&'a str, Cow<'a, str>>, |
468 |
| - } |
469 |
| - |
470 |
| - impl<'a> Pkcs11Uri<'a> { |
471 |
| - pub fn parse(uri: &'a str) -> anyhow::Result<Self> { |
472 |
| - let path = uri |
473 |
| - .strip_prefix("pkcs11:") |
474 |
| - .ok_or_else(|| anyhow::anyhow!("missing PKCS #11 URI scheme"))?; |
475 |
| - |
476 |
| - // split of the query component |
477 |
| - let path = path.split_once('?').map(|(l, _)| l).unwrap_or(path); |
478 |
| - |
479 |
| - // parse attributes, duplicate attributes are an error (RFC section 2.3) |
480 |
| - let pairs_iter = path.split(';').filter_map(|pair| pair.split_once('=')); |
481 |
| - let mut pairs: HashMap<&str, &str> = HashMap::new(); |
482 |
| - for (k, v) in pairs_iter { |
483 |
| - let prev_value = pairs.insert(k, v); |
484 |
| - if prev_value.is_some() { |
485 |
| - anyhow::bail!("PKCS#11 URI contains duplicate attribute ({k})"); |
486 |
| - } |
487 |
| - } |
488 |
| - |
489 |
| - let token = pairs |
490 |
| - .remove("token") |
491 |
| - .map(|v| percent_encoding::percent_decode_str(v).decode_utf8_lossy()); |
492 |
| - let serial = pairs |
493 |
| - .remove("serial") |
494 |
| - .map(|v| percent_encoding::percent_decode_str(v).decode_utf8_lossy()); |
495 |
| - let object = pairs |
496 |
| - .remove("object") |
497 |
| - .map(|v| percent_encoding::percent_decode_str(v).decode_utf8_lossy()); |
498 |
| - |
499 |
| - let id: Option<Vec<u8>> = pairs |
500 |
| - .remove("id") |
501 |
| - .map(|id| percent_encoding::percent_decode_str(id).collect()); |
502 |
| - |
503 |
| - let other = pairs |
504 |
| - .into_iter() |
505 |
| - .map(|(k, v)| { |
506 |
| - ( |
507 |
| - k, |
508 |
| - percent_encoding::percent_decode_str(v).decode_utf8_lossy(), |
509 |
| - ) |
510 |
| - }) |
511 |
| - .collect(); |
512 |
| - |
513 |
| - Ok(Self { |
514 |
| - token, |
515 |
| - serial, |
516 |
| - id, |
517 |
| - object, |
518 |
| - other, |
519 |
| - }) |
520 |
| - } |
521 |
| - |
522 |
| - /// Add new attributes from `other` to `self`. |
523 |
| - /// |
524 |
| - /// If other contains new attributes not present in self, add them to self. If these |
525 |
| - /// attributes are already present in self, preserve value currently in self. |
526 |
| - pub fn append_attributes(&mut self, other: Self) { |
527 |
| - self.token = self.token.take().or(other.token); |
528 |
| - self.serial = self.serial.take().or(other.serial); |
529 |
| - self.id = self.id.take().or(other.id); |
530 |
| - self.object = self.object.take().or(other.object); |
531 |
| - |
532 |
| - for (attribute, value) in other.other { |
533 |
| - if !self.other.contains_key(attribute) { |
534 |
| - self.other.insert(attribute, value); |
535 |
| - } |
536 |
| - } |
537 |
| - } |
538 |
| - } |
539 |
| - |
540 |
| - #[cfg(test)] |
541 |
| - mod tests { |
542 |
| - use super::*; |
543 |
| - |
544 |
| - #[test] |
545 |
| - fn decodes_valid_pkcs11_uri() { |
546 |
| - // test input URIs taken from RFC examples section and combined with properties we actually use |
547 |
| - // https://www.rfc-editor.org/rfc/rfc7512.html#section-3 |
548 |
| - let input = "pkcs11:token=The%20Software%20PKCS%2311%20Softtoken;\ |
549 |
| - manufacturer=Snake%20Oil,%20Inc.;\ |
550 |
| - model=1.0;\ |
551 |
| - object=my-certificate;\ |
552 |
| - type=cert;\ |
553 |
| - id=%69%95%3E%5C%F4%BD%EC%91;\ |
554 |
| - serial=\ |
555 |
| - ?pin-source=file:/etc/token_pin"; |
556 |
| - |
557 |
| - let attributes = Pkcs11Uri::parse(input).unwrap(); |
558 |
| - |
559 |
| - assert_eq!(attributes.token.unwrap(), "The Software PKCS#11 Softtoken"); |
560 |
| - assert_eq!( |
561 |
| - attributes.other.get("manufacturer").unwrap(), |
562 |
| - "Snake Oil, Inc." |
563 |
| - ); |
564 |
| - assert_eq!(attributes.other.get("model").unwrap(), "1.0"); |
565 |
| - assert_eq!(attributes.serial.unwrap(), ""); |
566 |
| - assert_eq!(attributes.object.unwrap(), "my-certificate"); |
567 |
| - assert_eq!( |
568 |
| - attributes.id, |
569 |
| - Some(vec![0x69, 0x95, 0x3e, 0x5c, 0xf4, 0xbd, 0xec, 0x91]) |
570 |
| - ); |
571 |
| - } |
572 |
| - |
573 |
| - #[test] |
574 |
| - fn fails_on_uris_with_duplicate_attributes() { |
575 |
| - let input = "pkcs11:token=my-token;token=my-token"; |
576 |
| - let err = Pkcs11Uri::parse(input).unwrap_err(); |
577 |
| - assert!(err |
578 |
| - .to_string() |
579 |
| - .contains("PKCS#11 URI contains duplicate attribute (token)")); |
580 |
| - } |
581 |
| - |
582 |
| - #[test] |
583 |
| - fn fails_on_uris_with_invalid_scheme() { |
584 |
| - let input = "not a pkcs#11 uri"; |
585 |
| - let err = Pkcs11Uri::parse(input).unwrap_err(); |
586 |
| - assert!(err.to_string().contains("missing PKCS #11 URI scheme")); |
587 |
| - } |
588 |
| - |
589 |
| - #[test] |
590 |
| - fn appends_attributes_correctly() { |
591 |
| - let mut uri1 = Pkcs11Uri::parse("pkcs11:token=token1").unwrap(); |
592 |
| - let uri2 = Pkcs11Uri::parse( |
593 |
| - "pkcs11:token=token2;serial=serial2;id=%01%02;object=object2;key1=value1", |
594 |
| - ) |
595 |
| - .unwrap(); |
596 |
| - |
597 |
| - uri1.append_attributes(uri2); |
598 |
| - |
599 |
| - assert_eq!(uri1.token.unwrap(), "token1"); |
600 |
| - assert_eq!(uri1.serial.unwrap(), "serial2"); |
601 |
| - assert_eq!(uri1.id, Some(vec![0x01, 0x02])); |
602 |
| - assert_eq!(uri1.object.unwrap(), "object2"); |
603 |
| - assert_eq!(uri1.other.get("key1").unwrap(), "value1"); |
604 |
| - } |
605 |
| - |
606 |
| - #[test] |
607 |
| - fn appends_attributes_with_no_conflicts() { |
608 |
| - let mut uri1 = Pkcs11Uri::parse("pkcs11:").unwrap(); |
609 |
| - let uri2 = Pkcs11Uri::parse( |
610 |
| - "pkcs11:token=token2;serial=serial2;id=%01%02;object=object2;key1=value1", |
611 |
| - ) |
612 |
| - .unwrap(); |
613 |
| - |
614 |
| - uri1.append_attributes(uri2); |
615 |
| - |
616 |
| - assert_eq!(uri1.token.unwrap(), "token2"); |
617 |
| - assert_eq!(uri1.serial.unwrap(), "serial2"); |
618 |
| - assert_eq!(uri1.id, Some(vec![0x01, 0x02])); |
619 |
| - assert_eq!(uri1.object.unwrap(), "object2"); |
620 |
| - assert_eq!(uri1.other.get("key1").unwrap(), "value1"); |
621 |
| - } |
622 |
| - |
623 |
| - #[test] |
624 |
| - fn does_not_override_existing_attributes() { |
625 |
| - let mut uri1 = Pkcs11Uri::parse( |
626 |
| - "pkcs11:token=token1;serial=serial1;id=%01;object=object1;key1=value1", |
627 |
| - ) |
628 |
| - .unwrap(); |
629 |
| - let uri2 = Pkcs11Uri::parse( |
630 |
| - "pkcs11:token=token2;serial=serial2;id=%02;object=object2;key2=value2", |
631 |
| - ) |
632 |
| - .unwrap(); |
633 |
| - |
634 |
| - uri1.append_attributes(uri2); |
635 |
| - |
636 |
| - assert_eq!(uri1.token.unwrap(), "token1"); |
637 |
| - assert_eq!(uri1.serial.unwrap(), "serial1"); |
638 |
| - assert_eq!(uri1.id, Some(vec![0x01])); |
639 |
| - assert_eq!(uri1.object.unwrap(), "object1"); |
640 |
| - assert_eq!(uri1.other.get("key1").unwrap(), "value1"); |
641 |
| - assert_eq!(uri1.other.get("key2").unwrap(), "value2"); |
642 |
| - } |
643 |
| - } |
644 |
| -} |
0 commit comments