From b5935f41a53a7ff58eac185d856a3f6ea5999b5a Mon Sep 17 00:00:00 2001 From: bkioshn <35752733+bkioshn@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:43:16 +0700 Subject: [PATCH] fix(dart/catalyst_cardano_serialization): x509 distinguished name structure (#1290) * fix: x509 distinguished name structure Signed-off-by: bkioshn * fix: format Signed-off-by: bkioshn * feat: make it possible to override ASN1 tag for subject alt name in the x509 cert * fix: static analysis issue --------- Signed-off-by: bkioshn Co-authored-by: Dominik Toton <166132265+dtscalac@users.noreply.github.com> Co-authored-by: Dominik Toton --- .../registration_transaction_builder.dart | 5 +- .../example/lib/sign_and_submit_rbac_tx.dart | 13 +-- .../lib/src/rbac/x509_certificate.dart | 90 ++++++++++++++----- .../test/rbac/x509_certificate_test.dart | 8 +- 4 files changed, 86 insertions(+), 30 deletions(-) diff --git a/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/registration/registration_transaction_builder.dart b/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/registration/registration_transaction_builder.dart index 3e60e15c51a..f273d45e187 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/registration/registration_transaction_builder.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/registration/registration_transaction_builder.dart @@ -157,7 +157,10 @@ final class RegistrationTransactionBuilder { subject: issuer, extensions: X509CertificateExtensions( subjectAltName: [ - 'web+cardano://addr/${_stakeAddress.toBech32()}', + X509String( + 'web+cardano://addr/${_stakeAddress.toBech32()}', + tag: X509String.uriTag, + ), ], ), ); diff --git a/catalyst_voices/packages/libs/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_rbac_tx.dart b/catalyst_voices/packages/libs/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_rbac_tx.dart index 2afc3ddae41..a3695842c7c 100644 --- a/catalyst_voices/packages/libs/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_rbac_tx.dart +++ b/catalyst_voices/packages/libs/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_rbac_tx.dart @@ -212,11 +212,14 @@ Future generateX509Certificate({ subject: issuer, extensions: X509CertificateExtensions( subjectAltName: [ - 'mydomain.com', - 'www.mydomain.com', - 'example.com', - 'www.example.com', - 'web+cardano://addr/${stakeAddress.toBech32()}', + const X509String('mydomain.com', tag: X509String.domainNameTag), + const X509String('www.mydomain.com', tag: X509String.domainNameTag), + const X509String('example.com', tag: X509String.domainNameTag), + const X509String('www.example.com', tag: X509String.domainNameTag), + X509String( + 'web+cardano://addr/${stakeAddress.toBech32()}', + tag: X509String.uriTag, + ), ], ), ); diff --git a/catalyst_voices/packages/libs/catalyst_cardano_serialization/lib/src/rbac/x509_certificate.dart b/catalyst_voices/packages/libs/catalyst_cardano_serialization/lib/src/rbac/x509_certificate.dart index dbf6f7c7eb1..a4a9e00ce9b 100644 --- a/catalyst_voices/packages/libs/catalyst_cardano_serialization/lib/src/rbac/x509_certificate.dart +++ b/catalyst_voices/packages/libs/catalyst_cardano_serialization/lib/src/rbac/x509_certificate.dart @@ -245,54 +245,72 @@ class X509DistinguishedName with EquatableMixin { final countryName = this.countryName; if (countryName != null) { sequence.add( - ASN1Sequence() - ..add(ASN1ObjectIdentifier.fromName('c')) - ..add(ASN1PrintableString(countryName)), + ASN1Set() + ..add( + ASN1Sequence() + ..add(ASN1ObjectIdentifier.fromName('c')) + ..add(ASN1PrintableString(countryName)), + ), ); } final stateOrProvinceName = this.stateOrProvinceName; if (stateOrProvinceName != null) { sequence.add( - ASN1Sequence() - ..add(ASN1ObjectIdentifier.fromName('st')) - ..add(ASN1PrintableString(stateOrProvinceName)), + ASN1Set() + ..add( + ASN1Sequence() + ..add(ASN1ObjectIdentifier.fromName('st')) + ..add(ASN1PrintableString(stateOrProvinceName)), + ), ); } final localityName = this.localityName; if (localityName != null) { sequence.add( - ASN1Sequence() - ..add(ASN1ObjectIdentifier.fromName('l')) - ..add(ASN1PrintableString(localityName)), + ASN1Set() + ..add( + ASN1Sequence() + ..add(ASN1ObjectIdentifier.fromName('l')) + ..add(ASN1PrintableString(localityName)), + ), ); } final organizationName = this.organizationName; if (organizationName != null) { sequence.add( - ASN1Sequence() - ..add(ASN1ObjectIdentifier.fromName('o')) - ..add(ASN1PrintableString(organizationName)), + ASN1Set() + ..add( + ASN1Sequence() + ..add(ASN1ObjectIdentifier.fromName('o')) + ..add(ASN1PrintableString(organizationName)), + ), ); } final organizationalUnitName = this.organizationalUnitName; if (organizationalUnitName != null) { sequence.add( - ASN1Sequence() - ..add(ASN1ObjectIdentifier.fromName('ou')) - ..add(ASN1PrintableString(organizationalUnitName)), + ASN1Set() + ..add( + ASN1Sequence() + ..add(ASN1ObjectIdentifier.fromName('ou')) + ..add(ASN1PrintableString(organizationalUnitName)), + ), ); } final commonName = this.commonName; if (commonName != null) { sequence.add( - ASN1Sequence() - ..add(ASN1ObjectIdentifier.fromName('cn')) - ..add(ASN1PrintableString(commonName)), + ASN1Set() + ..add( + ASN1Sequence() + ..add(ASN1ObjectIdentifier.fromName('cn')) + ..add(ASN1PrintableString(commonName)), + ), ); } @@ -313,7 +331,7 @@ class X509DistinguishedName with EquatableMixin { /// Extra extensions of the certificate. class X509CertificateExtensions with EquatableMixin { /// List of alternative subject names. - final List? subjectAltName; + final List? subjectAltName; /// The default constructor for the [X509CertificateExtensions]. const X509CertificateExtensions({this.subjectAltName}); @@ -330,7 +348,7 @@ class X509CertificateExtensions with EquatableMixin { if (subjectAltName != null) { final subjectAltNameSequence = ASN1Sequence(); for (final name in subjectAltName) { - subjectAltNameSequence.add(ASN1OctetString(name)); + subjectAltNameSequence.add(name.toASN1()); } extensionsSequence.add( @@ -350,3 +368,35 @@ class X509CertificateExtensions with EquatableMixin { @override List get props => [subjectAltName]; } + +/// Represents an ASN1 encodable string +/// that can be optionally tagged with [tag]. +class X509String with EquatableMixin { + /// An ASN1 tag for the uris. + static const int uriTag = 0x86; + + /// An ASN1 tag for domain names. + static const int domainNameTag = 0x82; + + /// The string value. + final String value; + + /// The optional ASN1 tag. + final int tag; + + /// The default constructor for the [X509String]. + const X509String( + this.value, { + this.tag = OCTET_STRING_TYPE, + }); + + /// Encodes the data in ASN1 format. + ASN1Object toASN1() { + _ensureASN1FrequentNamesRegistered(); + + return ASN1OctetString(value, tag: tag); + } + + @override + List get props => [value, tag]; +} diff --git a/catalyst_voices/packages/libs/catalyst_cardano_serialization/test/rbac/x509_certificate_test.dart b/catalyst_voices/packages/libs/catalyst_cardano_serialization/test/rbac/x509_certificate_test.dart index 298ba9cd092..0c719fb2ad1 100644 --- a/catalyst_voices/packages/libs/catalyst_cardano_serialization/test/rbac/x509_certificate_test.dart +++ b/catalyst_voices/packages/libs/catalyst_cardano_serialization/test/rbac/x509_certificate_test.dart @@ -29,10 +29,10 @@ void main() { subject: issuer, extensions: const X509CertificateExtensions( subjectAltName: [ - 'mydomain.com', - 'www.mydomain.com', - 'example.com', - 'www.example.com', + X509String('mydomain.com', tag: X509String.domainNameTag), + X509String('www.mydomain.com', tag: X509String.domainNameTag), + X509String('example.com', tag: X509String.domainNameTag), + X509String('www.example.com', tag: X509String.domainNameTag), ], ), );