Skip to content

Commit

Permalink
feat: QCStatements extension (pull request #15 from jeroentrappers)
Browse files Browse the repository at this point in the history
  • Loading branch information
rbellens committed Nov 3, 2022
1 parent 27cc8f5 commit a5bbd73
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 3 deletions.
44 changes: 44 additions & 0 deletions lib/src/extension.dart
Expand Up @@ -101,6 +101,8 @@ abstract class ExtensionValue {
switch (id.nodes.last) {
case 1:
return AuthorityInformationAccess.fromAsn1(obj as ASN1Sequence);
case 3:
return QCStatements.fromAsn1(obj as ASN1Sequence);
}
}
throw UnimplementedError(
Expand Down Expand Up @@ -676,6 +678,48 @@ class AuthorityInformationAccess extends ExtensionValue {
}
}

class QCStatements extends ExtensionValue {
final List<QCStatement> statements;

QCStatements({required this.statements});

factory QCStatements.fromAsn1(ASN1Sequence sequence) {
return QCStatements(statements: [
for (var i in sequence.elements) QCStatement.fromAsn1(i as ASN1Sequence)
]);
}
}

class QCStatement {
final ObjectIdentifier statementId;
final dynamic qcStatementInfo;

/// The ASN.1 definition is:
///
/// QCStatement ::= SEQUENCE {
/// statementId OBJECT IDENTIFIER,
/// statementInfo ANY DEFINED BY statementId OPTIONAL}
QCStatement({required this.statementId, required this.qcStatementInfo});

factory QCStatement.fromAsn1(ASN1Sequence sequence) {
var statementId = ObjectIdentifier.fromAsn1(
ASN1ObjectIdentifier.fromBytes(sequence.elements[0].encodedBytes));

dynamic qcStatementInfo;
if (sequence.elements.length > 1) {
qcStatementInfo = toDart(sequence.elements[1]);
}

return QCStatement(
statementId: statementId, qcStatementInfo: qcStatementInfo);
}

@override
String toString() {
return 'QCStatement{statementId: $statementId, qcStatementInfo: $qcStatementInfo}';
}
}

class AccessDescription {
final ObjectIdentifier? accessMethod;
final String? accessLocation;
Expand Down
55 changes: 52 additions & 3 deletions lib/src/objectidentifier.dart
Expand Up @@ -71,7 +71,13 @@ class ObjectIdentifier {
}

@override
String toString() => name;
String toString() {
try {
return name;
} on UnknownOIDNameError {
return nodes.join('.');
}
}

static const _tree = {
0: {
Expand All @@ -91,6 +97,44 @@ class ObjectIdentifier {
}
}
}
},
4: {
null: 'identified-organization',
0: {
null: 'etsi',
1862: {
null: 'qc-profile',
0: {
null: 'id-mod',
2: 'id-mod-qc-profile-2',
},
1: {
null: 'qcs',
1: 'qcs-QcCompliance',
2: 'qcs-QcLimitValue',
3: 'qcs-QcRetentionPeriod',
4: 'qcs-QcSSCD',
5: 'qcs-QcPDS',
6: {
null: 'qcs-QcType',
1: 'qct-esign',
2: 'qct-eseal',
3: 'qct-web',
}
}
},
194121: {
null: 'qualified-certificate-policies',
1: {
null: 'policy-identifiers',
0: 'qcp-natural',
1: 'qcp-legal',
2: 'qcp-natural-qscd',
3: 'qcp-legal-qscd',
4: 'qcp-web',
}
}
}
}
},
1: {
Expand Down Expand Up @@ -282,6 +326,11 @@ class ObjectIdentifier {
4: 'emailProtection',
8: 'timeStamping',
9: 'OCSPSigning'
},
11: {
null: 'qcs',
1: 'pkixQCSyntax-v1',
2: 'id-qcs-pkixQCSyntax-v2',
}
}
}
Expand Down Expand Up @@ -498,5 +547,5 @@ class ObjectIdentifier {
// Throw when There OID name is unknown.
// For example, be defined unique extension.
class UnknownOIDNameError extends StateError {
UnknownOIDNameError(String message): super(message);
}
UnknownOIDNameError(String message) : super(message);
}
22 changes: 22 additions & 0 deletions test/x509_test.dart
Expand Up @@ -344,6 +344,28 @@ MIIDJTCCAsygAwIBAgIIMWAojeoOOlkwCgYIKoZIzj0EAwIwgYYxFzAVBgNVBAMMDkNTQ0EgSGVhbHRo
expect(pkup.notBefore, DateTime.parse('2021-04-26 08:57:35.000Z'));
expect(pkup.notAfter, DateTime.parse('2021-11-22 08:57:35.000Z'));
});

test('parse certificate with QCStatement extension', () {
var pem = '''-----BEGIN CERTIFICATE-----
MIIIHzCCB8WgAwIBAgIJf35N0O0if7S5MAoGCCqGSM49BAMCMIGwMT8wPQYDVQQDDDZFQURUcnVzdCBFQ0MgMjU2IFN1YkNBIEZvciBRdWFsaWZpZWQgQ2VydGlmaWNhdGVzIDIwMTkxLzAtBgNVBAoMJkV1cm9wZWFuIEFnZW5jeSBvZiBEaWdpdGFsIFRydXN0LCBTLkwuMQswCQYDVQQGEwJFUzEYMBYGA1UEYQwPVkFURVMtQjg1NjI2MjQwMRUwEwYDVQQLDAxMZWdhbCBQZXJzb24wHhcNMjEwNDI1MjMxMDM3WhcNMjYwNDI0MjMxMDM3WjCCAQYxNTAzBgNVBAMMLFBMQVRBRk9STUEgREUgVkFMSURBQ0lPTiBZIEZJUk1BIEVMRUNUUk9OSUNBMREwDwYDVQQFEwhTMjgzMzAwMjEQMA4GA1UEKgwHQU5UT05JTzEhMB8GA1UEBAwYUEVSRVogR09OWkFMRVogMTIzNDU2NzhaMRowGAYDVQQLDBFTRUxMTyBFTEVDVFJPTklDTzESMBAGA1UECwwJRTEyMzQ1Njc4MRcwFQYDVQQLDA5TVUJESVJFQ0NJT04gWDEXMBUGA1UEYQwOVkFURVMtUzI4MzMwMDIxFjAUBgNVBAoMDUVOVElEQURBIFMuTC4xCzAJBgNVBAYTAkVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeMLmnzwEz2ccCnIcpheqC6mcoT/Wwh3mrsqhhCZ70lROxuNrNmXALgx+NpBzl01T5zK91RuAedmfh0mxl3EmQKOCBW0wggVpMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU00xsOr02/nCHI4c67j2Qz8ub9yEweQYIKwYBBQUHAQEEbTBrMEQGCCsGAQUFBzAChjhodHRwOi8vY2EuZWFkdHJ1c3QuZXUvZWFkdHJ1c3Qtc3ViY2EtZWNjMjU2ZWFkbHAyMDE5LmNydDAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZWFkdHJ1c3QuZXUwYwYDVR0SBFwwWoEOY2FAZWFkdHJ1c3QuZXWGFmh0dHA6Ly93d3cuZWFkdHJ1c3QuZXWGFWh0dHA6Ly9jYS5lYWR0cnVzdC5ldYYZaHR0cDovL3BvbGljeS5lYWR0cnVzdC5ldTCCAV8GA1UdEQSCAVYwggFSgRlhbnRvbmlvY29ycmVvQGVqZW1wbG8uY29tpIIBMzCCAS8xKDAmBglghVQBAwUGAQkMGWFudG9uaW9jb3JyZW9AZWplbXBsby5jb20xFzAVBglghVQBAwUGAQgMCEdPTlpBTEVaMRQwEgYJYIVUAQMFBgEHDAVQRVJFWjEWMBQGCWCFVAEDBQYBBgwHQU5UT05JTzE7MDkGCWCFVAEDBQYBBQwsUExBVEFGT1JNQSBERSBWQUxJREFDSU9OIFkgRklSTUEgRUxFQ1RST05JQ0ExGDAWBglghVQBAwUGAQQMCTEyMzQ1Njc4WjEXMBUGCWCFVAEDBQYBAwwIUzI4MzMwMDIxHDAaBglghVQBAwUGAQIMDUVOVElEQURBIFMuTC4xLjAsBglghVQBAwUGAQEMH1NFTExPIEVMRUNUUk9OSUNPIERFIE5JVkVMIEFMVE8wggGLBgNVHSAEggGCMIIBfjBvBgcEAIvsQAEDMGQwYgYIKwYBBQUHAgIwVgxURXVyb3BlYW4gVGVsZWNvbW11bmljYXRpb25zIFN0YW5kYXJkcyBJbnN0aXR1dGUuIGVJREFTIEV1cm9wZWFuIFJlZ3VsYXRpb24gQ29tcGxpYW50MIH+Bg4rBgEEAYN1AgEBAYLCETCB6zCBwQYIKwYBBQUHAgIwgbQMgbFDZXJ0aWZpY2FkbyBjdWFsaWZpY2FkbyBkZSBzZWxsbyBlbGVjdHLDs25pY28gZGUgQWRtaW5pc3RyYWNpw7NuLCDDs3JnYW5vIG8gZW50aWRhZCBkZSBkZXJlY2hvIHDDumJsaWNvLCBuaXZlbCBhbHRvLiBDb25zdWx0ZSBsYXMgY29uZGljaW9uZXMgZGUgdXNvIGVuIGh0dHA6Ly9wb2xpY3kuZWFkdHJ1c3QuZXUwJQYIKwYBBQUHAgEWGWh0dHA6Ly9wb2xpY3kuZWFkdHJ1c3QuZXUwCgYIYIVUAQMFBgEwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMIHLBggrBgEFBQcBAwSBvjCBuzAVBggrBgEFBQcLAjAJBgcEAIvsSQECMAgGBgQAjkYBATALBgYEAI5GAQMCAQ8wCAYGBACORgEEMBMGBgQAjkYBBjAJBgcEAI5GAQYCMGwGBgQAjkYBBTBiMDAWKmh0dHBzOi8vZWFkdHJ1c3QuZXUvZW4vZG9jdW1lbnRzLWluLWZvcmNlLxMCZW4wLhYoaHR0cHM6Ly9lYWR0cnVzdC5ldS9kb2N1bWVudG9zLXZpZ2VudGVzLxMCZXMwSgYDVR0fBEMwQTA/oD2gO4Y5aHR0cDovL2NybC5lYWR0cnVzdC5ldS9lYWR0cnVzdC1zdWJjYS1lY2MyNTZlYWRscDIwMTkuY3JsMB0GA1UdDgQWBBQ/OLscGZ+Pg4CrckvYnPnShHYhQjAOBgNVHQ8BAf8EBAMCBeAwCgYIKoZIzj0EAwIDSAAwRQIhAKdQE7I7ELKgEnAyxyKJ7RJDB8ON9zauptkK6T77K+9GAiAVcpJa0xiiQaSq4PoDy/XZ2y/QF58Sh3uNv691aBClSA==
-----END CERTIFICATE-----''';

var cert = parsePem(pem).single;
expect(cert, isA<X509Certificate>());

var c = cert as X509Certificate;
// get extension value
var ext = c.tbsCertificate.extensions!
.map((e) => e.extnValue)
.whereType<QCStatements>()
.first;

expect(ext.statements.length, 6);
expect(ext.statements.last.qcStatementInfo, [
['https://eadtrust.eu/en/documents-in-force/', 'en'],
['https://eadtrust.eu/documentos-vigentes/', 'es']
]);
});
});

group('PolicyInformation', () {
Expand Down

0 comments on commit a5bbd73

Please sign in to comment.