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:: BigInt ;
9
+ use asn1_rs:: FromDer as _;
10
+ use asn1_rs:: Integer ;
11
+ use asn1_rs:: SequenceOf ;
11
12
use asn1_rs:: ToDer ;
12
13
use camino:: Utf8PathBuf ;
13
14
use cryptoki:: context:: CInitializeArgs ;
@@ -35,6 +36,10 @@ use std::fmt::Debug;
35
36
use std:: sync:: Arc ;
36
37
use std:: sync:: Mutex ;
37
38
39
+ pub use cryptoki:: types:: AuthPin ;
40
+
41
+ mod uri;
42
+
38
43
// oIDs for curves defined here: https://datatracker.ietf.org/doc/html/rfc5480#section-2.1.1.1
39
44
// other can be browsed here: https://oid-base.com/get/1.3.132.0.34
40
45
const SECP256R1_OID : & str = "1.2.840.10045.3.1.7" ;
@@ -395,19 +400,32 @@ impl Signer for PkcsSigner {
395
400
}
396
401
}
397
402
398
- fn format_asn1_ecdsa_signature ( r_bytes : & [ u8 ] , s_bytes : & [ u8 ] ) -> Result < Vec < u8 > , anyhow:: Error > {
399
- let mut writer = Vec :: new ( ) ;
400
-
401
- write_asn1_integer ( & mut writer, r_bytes) ;
402
-
403
- write_asn1_integer ( & mut writer, s_bytes) ;
404
-
405
- let seq = asn1_rs:: Sequence :: new ( writer. into ( ) ) ;
406
- let b = seq. to_der_vec ( ) . unwrap ( ) ;
407
- Ok ( b)
403
+ /// Formats the output of PKCS11 EC signature as an ASN.1 Ecdsa-Sig-Value.
404
+ ///
405
+ /// This function takes the raw `r` and `s` byte slices and encodes them as ASN.1 INTEGERs,
406
+ /// then wraps them in an ASN.1 SEQUENCE, and finally serializes the structure to DER.
407
+ ///
408
+ /// PKCS#11 EC signature operations typically return a raw concatenation of the `r` and `s` values,
409
+ /// each representing a big-endian positive integer of fixed length (depending on the curve).
410
+ /// However, most cryptographic protocols (including TLS and X.509) expect ECDSA signatures to be
411
+ /// encoded as an ASN.1 DER SEQUENCE of two INTEGERs, as described in RFC 3279 section 2.2.3.
412
+ ///
413
+ /// - https://docs.oasis-open.org/pkcs11/pkcs11-curr/v3.0/os/pkcs11-curr-v3.0-os.html#_Toc30061178
414
+ /// - https://www.ietf.org/rfc/rfc3279#section-2.2.3
415
+ fn format_asn1_ecdsa_signature ( r_bytes : & [ u8 ] , s_bytes : & [ u8 ] ) -> anyhow:: Result < Vec < u8 > > {
416
+ let r = format_asn1_integer ( r_bytes) . to_signed_bytes_be ( ) ;
417
+ let r = Integer :: new ( & r) ;
418
+ let s = format_asn1_integer ( s_bytes) . to_signed_bytes_be ( ) ;
419
+ let s = Integer :: new ( & s) ;
420
+
421
+ let seq = SequenceOf :: < Integer > :: from_iter ( [ r, s] ) ;
422
+ let seq_der = seq
423
+ . to_der_vec ( )
424
+ . context ( "Unexpected ASN.1 error when serializing Ecdsa-Sig-Value" ) ?;
425
+ Ok ( seq_der)
408
426
}
409
427
410
- fn write_asn1_integer ( writer : & mut dyn std :: io :: Write , b : & [ u8 ] ) {
428
+ fn format_asn1_integer ( b : & [ u8 ] ) -> BigInt {
411
429
let mut i = asn1_rs:: BigInt :: from_signed_bytes_be ( b) ;
412
430
if i. sign ( ) == asn1_rs:: Sign :: Minus {
413
431
// Prepend a most significant zero byte if value < 0
@@ -416,9 +434,7 @@ fn write_asn1_integer(writer: &mut dyn std::io::Write, b: &[u8]) {
416
434
417
435
i = asn1_rs:: BigInt :: from_signed_bytes_be ( & positive) ;
418
436
}
419
- let i = i. to_signed_bytes_be ( ) ;
420
- let i = asn1_rs:: Integer :: new ( & i) ;
421
- let _ = i. write_der ( writer) ;
437
+ i
422
438
}
423
439
424
440
fn get_ec_mechanism ( session : & Session , key : ObjectHandle ) -> anyhow:: Result < SigScheme > {
@@ -449,196 +465,33 @@ fn get_ec_mechanism(session: &Session, key: ObjectHandle) -> anyhow::Result<SigS
449
465
}
450
466
}
451
467
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
- }
468
+ #[ cfg( test) ]
469
+ mod tests {
470
+ use super :: * ;
471
+ use asn1_rs:: Any ;
472
+ use asn1_rs:: Integer ;
473
+ use asn1_rs:: SequenceOf ;
474
+
475
+ #[ test]
476
+ fn test_format_asn1_ecdsa_signature_invalid_asn1 ( ) {
477
+ // Use 32-byte r and s (as for P-256)
478
+ let r = [ 0x01u8 ; 32 ] ;
479
+ let s = [ 0xffu8 ; 32 ] ;
480
+
481
+ let der = format_asn1_ecdsa_signature ( & r, & s) . expect ( "Should encode" ) ;
482
+
483
+ // Try to parse as ASN.1 SEQUENCE of two INTEGERs
484
+ let parsed = Any :: from_der ( & der) ;
485
+ assert ! ( parsed. is_ok( ) , "Should parse as ASN.1" ) ;
486
+
487
+ // Now check that the sequence contains exactly two INTEGERs
488
+ let seq: SequenceOf < Integer > = SequenceOf :: from_der ( & der)
489
+ . expect ( "Should parse as sequence" )
490
+ . 1 ;
491
+ assert_eq ! ( seq. len( ) , 2 , "ASN.1 sequence should have two items" ) ;
492
+
493
+ // make sure input is not misinterpreted as negative numbers
494
+ assert_eq ! ( seq[ 0 ] . as_bigint( ) . to_bytes_be( ) . 1 , r) ;
495
+ assert_eq ! ( seq[ 1 ] . as_bigint( ) . to_bytes_be( ) . 1 , s) ;
643
496
}
644
497
}
0 commit comments