@@ -3,15 +3,15 @@ use std::collections::HashMap;
33use accounts:: { Account , KeyPair } ;
44use asn1:: SubjectPublicKeyInfo ;
55use crypto:: bigint:: U256 ;
6- use crypto:: prelude:: { CryptoSignerWithOptions , ExposeSecret , SignatureEncoding } ;
7- use x509:: certificates:: { Certificate as X509Certificate , CertificateBuilder as X509CertificateBuilder , Extension } ;
6+ use crypto:: prelude:: { CryptoSignerWithOptions , SignatureEncoding } ;
7+ use x509:: certificates:: { CertificateBuilder as X509CertificateBuilder , Extension } ;
88use x509:: DistinguishedName ;
99
1010use crate :: asn1:: utils:: get_sensitive_attribute_oid;
1111use crate :: asn1:: KYC_ATTRIBUTES_EXTENSION_OID ;
12- use crate :: certificates:: error :: CertificateError ;
13- use crate :: kyc_schema:: { Attribute , AttributeBuilder , KYCAttributes } ;
14- use crate :: sensitive_attributes:: { SensitiveAttribute , SensitiveAttributeBuilder } ;
12+ use crate :: certificates:: { Certificate , CertificateError } ;
13+ use crate :: kyc_schema:: { AttributeBuilder , KYCAttributes } ;
14+ use crate :: sensitive_attributes:: SensitiveAttributeBuilder ;
1515
1616/// Extended certificate builder that supports KYC attributes.
1717///
@@ -140,14 +140,10 @@ impl CertificateBuilder {
140140 /// This method creates the X.509 certificate and includes any KYC attributes
141141 /// as a custom extension. Sensitive attributes are encrypted using the
142142 /// subject's keypair.
143- pub fn build < T : KeyPair , S > (
144- mut self ,
145- subject_keypair : & T ,
146- signing_keypair : & T ,
147- ) -> Result < Certificate , CertificateError >
143+ pub fn build < T , S > ( mut self , subject_keypair : & T , signing_keypair : & T ) -> Result < Certificate , CertificateError >
148144 where
149145 Account < T > : TryFrom < accounts:: Accountable < T > , Error = accounts:: AccountError > ,
150- T : CryptoSignerWithOptions < S > + ' static ,
146+ T : KeyPair + CryptoSignerWithOptions < S > + ' static ,
151147 S : SignatureEncoding ,
152148 {
153149 // If we have KYC attributes, add them as an extension
@@ -212,119 +208,12 @@ impl Default for CertificateBuilder {
212208 }
213209}
214210
215- /// Extended certificate that supports KYC attributes
216- #[ derive( Debug , Clone ) ]
217- pub struct Certificate {
218- /// The underlying X.509 certificate
219- inner : X509Certificate ,
220- /// Parsed KYC attributes from the certificate
221- kyc_attributes : KYCAttributes ,
222- // TODO: Fix dyn KeyPair issue
223- // subject_keypair: Option<Box<dyn KeyPair>>,
224- }
225-
226- impl Certificate {
227- /// Create a new certificate wrapper
228- pub fn new ( x509_cert : X509Certificate ) -> Self {
229- let kyc_attributes = Self :: parse_kyc_attributes ( & x509_cert) ;
230-
231- Self {
232- inner : x509_cert,
233- kyc_attributes,
234- // subject_keypair: None,
235- }
236- }
237-
238- /// Get the underlying X.509 certificate
239- pub fn to_x509 ( & self ) -> & X509Certificate {
240- & self . inner
241- }
242-
243- /// Get the parsed KYC attributes
244- pub fn kyc_attributes ( & self ) -> & KYCAttributes {
245- & self . kyc_attributes
246- }
247-
248- /// Get a specific KYC attribute by name
249- pub fn get_kyc_attribute ( & self , name : & str ) -> Option < & Attribute > {
250- let oid = get_sensitive_attribute_oid ( name) . ok ( ) ?;
251- self . kyc_attributes . find_by_object_identifier ( & oid)
252- }
253-
254- /// Decrypt a sensitive KYC attribute value
255- pub fn decrypt_kyc_attribute < T : KeyPair > ( & self , name : & str , keypair : & T ) -> Result < Vec < u8 > , CertificateError >
256- where
257- Account < T > : TryFrom < accounts:: Accountable < T > , Error = accounts:: AccountError > ,
258- {
259- let attribute = self
260- . get_kyc_attribute ( name)
261- . ok_or_else ( || CertificateError :: AttributeNotFound { name : name. to_string ( ) } ) ?;
262-
263- if !attribute. is_sensitive ( ) {
264- return Err ( CertificateError :: InvalidAttributeValue {
265- name : name. to_string ( ) ,
266- reason : "Attribute is not sensitive" . to_string ( ) ,
267- } ) ;
268- }
269-
270- // Decode the sensitive attribute from DER
271- let sensitive_attr: SensitiveAttribute = rasn:: der:: decode ( attribute. as_ref ( ) ) ?;
272-
273- // Decrypt the value
274- let decrypted = sensitive_attr. decrypt ( keypair) ?;
275- Ok ( decrypted. expose_secret ( ) . clone ( ) )
276- }
277-
278- /// Get a plain text KYC attribute value
279- pub fn get_plain_kyc_attribute ( & self , name : & str ) -> Result < Vec < u8 > , CertificateError > {
280- let attribute = self
281- . get_kyc_attribute ( name)
282- . ok_or_else ( || CertificateError :: AttributeNotFound { name : name. to_string ( ) } ) ?;
283-
284- if attribute. is_sensitive ( ) {
285- return Err ( CertificateError :: InvalidAttributeValue {
286- name : name. to_string ( ) ,
287- reason : "Attribute is sensitive and requires decryption" . to_string ( ) ,
288- } ) ;
289- }
290-
291- Ok ( attribute. as_ref ( ) . to_vec ( ) )
292- }
293-
294- /// Parse KYC attributes from an X.509 certificate
295- fn parse_kyc_attributes ( x509_cert : & X509Certificate ) -> KYCAttributes {
296- // Try to find the KYC attributes extension
297- if let Some ( extension) = x509_cert. get_extension ( KYC_ATTRIBUTES_EXTENSION_OID . to_string ( ) ) {
298- // Try to decode the extension value
299- if let Ok ( kyc_attrs) = rasn:: der:: decode :: < KYCAttributes > ( extension. value . as_bytes ( ) ) {
300- return kyc_attrs;
301- }
302- }
303-
304- // Return empty attributes if not found or parsing failed
305- KYCAttributes :: new ( )
306- }
307-
308- /// Check if the certificate has any KYC attributes
309- pub fn has_kyc_attributes ( & self ) -> bool {
310- !self . kyc_attributes . is_empty ( )
311- }
312-
313- /// Get the number of KYC attributes
314- pub fn kyc_attribute_count ( & self ) -> usize {
315- self . kyc_attributes . count ( )
316- }
317- }
318-
319211#[ cfg( test) ]
320212mod tests {
321- use accounts:: Account ;
322213 use x509:: certificates:: ExtensionBuilder ;
323- use x509:: utils:: create_dn;
324214 use x509:: DistinguishedName ;
325215
326216 use super :: * ;
327- use crate :: testing:: create_account_from_seed;
328217
329218 const TEST_ATTRIBUTES : & [ ( & str , & str , bool ) ] = & [
330219 ( "fullName" , "John Doe" , false ) ,
@@ -334,34 +223,17 @@ mod tests {
334223 ( "phoneNumber" , "+1-555-123-4567" , false ) ,
335224 ] ;
336225
337- /// Helper function to create a test X.509 certificate.
338- fn create_test_x509_cert ( ) -> X509Certificate {
339- // Create a minimal X.509 certificate for testing
340- let subject_dn = create_dn ( & [ ( x509:: oids:: CN , "Test" ) ] ) . unwrap ( ) ;
341- let account = create_account_from_seed :: < accounts:: KeyECDSASECP256K1 > ( 0 ) ;
342- let public_key = account. keypair . to_public_key ( ) . unwrap ( ) ;
343-
344- X509CertificateBuilder :: new ( )
345- . with_subject_dn ( subject_dn. clone ( ) )
346- . with_issuer_dn ( subject_dn)
347- . with_subject_public_key ( public_key. into ( ) )
348- . with_serial_number ( U256 :: from ( 1u64 ) )
349- . with_validity_days ( 365 )
350- . build ( & account. keypair )
351- . unwrap ( )
352- }
353-
354226 #[ test]
355227 fn test_certificate_builder_creation ( ) {
356228 let builders = [
357- ( "new" , CertificateBuilder :: new ( ) ) ,
358- ( "for_end_entity" , CertificateBuilder :: for_end_entity ( ) ) ,
359- ( "for_ca" , CertificateBuilder :: for_ca ( ) ) ,
360- ( "default" , CertificateBuilder :: default ( ) ) ,
229+ CertificateBuilder :: new ( ) ,
230+ CertificateBuilder :: for_end_entity ( ) ,
231+ CertificateBuilder :: for_ca ( ) ,
232+ CertificateBuilder :: default ( ) ,
361233 ] ;
362234
363- for ( name , builder) in builders {
364- assert ! ( builder. kyc_attributes. is_empty( ) , "Builder {name} should have empty attributes" ) ;
235+ for builder in builders {
236+ assert ! ( builder. kyc_attributes. is_empty( ) ) ;
365237 }
366238 }
367239
@@ -417,118 +289,4 @@ mod tests {
417289 assert ! ( matches!( result. unwrap_err( ) , CertificateError :: Asn1Error { .. } ) ) ;
418290 }
419291 }
420-
421- #[ test]
422- fn test_certificate_without_kyc_attributes ( ) {
423- let cert = Certificate :: new ( create_test_x509_cert ( ) ) ;
424- assert ! ( !cert. has_kyc_attributes( ) ) ;
425- assert_eq ! ( cert. kyc_attribute_count( ) , 0 ) ;
426- assert ! ( cert. get_kyc_attribute( "fullName" ) . is_none( ) ) ;
427-
428- // Test Certificate.to_x509
429- let x509_cert = cert. to_x509 ( ) ;
430- // Just check that we can access the X509 certificate
431- assert ! ( x509_cert
432- . get_extension( KYC_ATTRIBUTES_EXTENSION_OID . to_string( ) )
433- . is_none( ) ) ;
434-
435- // Test Certificate.kyc_attributes
436- let kyc_attrs = cert. kyc_attributes ( ) ;
437- assert_eq ! ( kyc_attrs. count( ) , 0 ) ;
438- }
439-
440- #[ test]
441- fn test_certificate_attribute_errors ( ) {
442- let cert = Certificate :: new ( create_test_x509_cert ( ) ) ;
443- let result = cert. get_plain_kyc_attribute ( "nonExistent" ) ;
444- assert ! ( result. is_err( ) ) ;
445- assert ! ( matches!( result. unwrap_err( ) , CertificateError :: AttributeNotFound { .. } ) ) ;
446- }
447-
448- fn test_certificate_building_functionality < T , S > ( account : Account < T > )
449- where
450- Account < T > : TryFrom < accounts:: Accountable < T > , Error = accounts:: AccountError > ,
451- T : KeyPair + CryptoSignerWithOptions < S > + ' static ,
452- S : SignatureEncoding ,
453- {
454- let subject_dn = x509:: utils:: create_dn ( & [ ( x509:: oids:: CN , "Test Subject" ) ] ) . unwrap ( ) ;
455- let public_key = account. keypair . to_public_key ( ) . unwrap ( ) ;
456- let mut builder = CertificateBuilder :: for_end_entity ( )
457- . with_subject_dn ( subject_dn. clone ( ) )
458- . with_issuer_dn ( subject_dn)
459- . with_serial_number ( U256 :: from ( 12345u64 ) )
460- . with_validity_days ( 365 )
461- . with_subject_public_key ( public_key. into ( ) ) ;
462-
463- // Add test attributes
464- for ( name, value, sensitive) in TEST_ATTRIBUTES . iter ( ) . take ( 2 ) {
465- builder = if * sensitive {
466- builder. with_sensitive_attribute ( name, value) . unwrap ( )
467- } else {
468- builder. with_plain_attribute ( name, value) . unwrap ( )
469- } ;
470- }
471-
472- let certificate = builder. build ( & account. keypair , & account. keypair ) . unwrap ( ) ;
473-
474- // Verify certificate has KYC attributes
475- assert ! ( certificate. has_kyc_attributes( ) ) ;
476- assert_eq ! ( certificate. kyc_attribute_count( ) , 2 ) ;
477-
478- // Test Certificate.kyc_attributes() method when KYC attributes are present
479- let kyc_attrs = certificate. kyc_attributes ( ) ;
480- assert_eq ! ( kyc_attrs. count( ) , 2 ) ;
481-
482- // Test both plain and sensitive attributes
483- for ( name, value, sensitive) in TEST_ATTRIBUTES . iter ( ) . take ( 2 ) {
484- if * sensitive {
485- let decrypted = certificate
486- . decrypt_kyc_attribute ( name, & account. keypair )
487- . unwrap ( ) ;
488- assert_eq ! ( decrypted, value. as_bytes( ) ) ;
489- } else {
490- let plain = certificate. get_plain_kyc_attribute ( name) . unwrap ( ) ;
491- assert_eq ! ( plain, value. as_bytes( ) ) ;
492- }
493- }
494-
495- // Test error cases
496- assert ! ( certificate. get_kyc_attribute( "nonExistent" ) . is_none( ) ) ;
497- }
498-
499- crate :: test_all_key_types!( test_certificate_building, test_certificate_building_functionality) ;
500-
501- fn test_certificate_attribute_type_errors < T , S > ( account : Account < T > )
502- where
503- Account < T > : TryFrom < accounts:: Accountable < T > , Error = accounts:: AccountError > ,
504- T : KeyPair + CryptoSignerWithOptions < S > + ' static ,
505- S : SignatureEncoding ,
506- {
507- let subject_dn = x509:: utils:: create_dn ( & [ ( x509:: oids:: CN , "Test Subject" ) ] ) . unwrap ( ) ;
508- let public_key = account. keypair . to_public_key ( ) . unwrap ( ) ;
509- let builder = CertificateBuilder :: for_end_entity ( )
510- . with_subject_dn ( subject_dn. clone ( ) )
511- . with_issuer_dn ( subject_dn)
512- . with_serial_number ( U256 :: from ( 12345u64 ) )
513- . with_validity_days ( 365 )
514- . with_subject_public_key ( public_key. into ( ) )
515- . with_plain_attribute ( "fullName" , "Jane Smith" )
516- . unwrap ( )
517- . with_sensitive_attribute ( "email" , "jane@example.com" )
518- . unwrap ( ) ;
519-
520- let certificate = builder. build ( & account. keypair , & account. keypair ) . unwrap ( ) ;
521-
522- // Test trying to decrypt a plain attribute
523- let result = certificate. decrypt_kyc_attribute ( "fullName" , & account. keypair ) ;
524- assert ! ( result. is_err( ) ) ;
525- assert ! ( matches!( result. unwrap_err( ) , CertificateError :: InvalidAttributeValue { .. } ) ) ;
526-
527- // Test trying to get a sensitive attribute as plain
528- let result = certificate. get_plain_kyc_attribute ( "email" ) ;
529- assert ! ( result. is_err( ) ) ;
530- assert ! ( matches!( result. unwrap_err( ) , CertificateError :: InvalidAttributeValue { .. } ) ) ;
531- }
532-
533- crate :: test_all_key_types!( test_certificate_type_errors, test_certificate_attribute_type_errors) ;
534292}
0 commit comments