diff --git a/lib/app/shared/extension/string_extension.dart b/lib/app/shared/extension/string_extension.dart index e57dcc269..d3b1f47f4 100644 --- a/lib/app/shared/extension/string_extension.dart +++ b/lib/app/shared/extension/string_extension.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; + +import 'package:convert/convert.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; @@ -34,4 +37,10 @@ extension StringExtension on String { } Characters get characters => Characters(this); + + String get char2Bytes { + final List encode = utf8.encode(this); + final String bytes = hex.encode(encode); + return bytes; + } } diff --git a/lib/app/shared/helper_functions/helper_functions.dart b/lib/app/shared/helper_functions/helper_functions.dart index b852d7552..ac7194c86 100644 --- a/lib/app/shared/helper_functions/helper_functions.dart +++ b/lib/app/shared/helper_functions/helper_functions.dart @@ -22,6 +22,9 @@ import 'package:key_generator/key_generator.dart'; import 'package:oidc4vc/oidc4vc.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:secure_storage/secure_storage.dart'; +import 'package:pointycastle/pointycastle.dart' as pc; +import 'package:asn1lib/asn1lib.dart' as asn1lib; +import 'package:x509/x509.dart' as x509; String generateDefaultAccountName( int accountIndex, @@ -81,23 +84,17 @@ String stringToHexPrefixedWith05({required String payload}) { payload, ].join(' '); - final String bytes = char2Bytes(formattedInput); + final String bytes = formattedInput.char2Bytes; const String prefix = '05'; const String stringIsHex = '0100'; - final String bytesOfByteLength = char2Bytes(bytes.length.toString()); + final String bytesOfByteLength = bytes.length.toString().char2Bytes; final payloadBytes = '$prefix$stringIsHex$bytesOfByteLength$bytes'; return payloadBytes; } -String char2Bytes(String text) { - final List encode = utf8.encode(text); - final String bytes = hex.encode(encode); - return bytes; -} - Future isConnected() async { final log = getLogger('Check Internet Connection'); @@ -1795,3 +1792,87 @@ List collectSdValues(Map data) { return result; } + +Future?> checkX509({ + required String encodedData, + required String clientId, + required JWTDecode jwtDecode, +}) async { + final Map header = + decodeHeader(jwtDecode: jwtDecode, token: encodedData); + + final x5c = header['x5c']; + + if (x5c != null) { + if (x5c is! List) { + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'x509_san_dns scheme error', + }, + ); + } + + //array x5c[0], it is a certificat in DER format (binary) + final certificate = x5c.firstOrNull; + + if (certificate == null) { + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'x509_san_dns scheme error', + }, + ); + } + + final decoded = base64Decode(certificate.toString()); + final seq = asn1lib.ASN1Sequence.fromBytes(decoded); + final cert = x509.X509Certificate.fromAsn1(seq); + + final subject = cert.tbsCertificate.subject; + + if (subject == null) { + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'x509_san_dns scheme error', + }, + ); + } + + final names = subject.names; + + if (names.isEmpty) { + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'x509_san_dns scheme error', + }, + ); + } + + final value = names[0].entries.map((element) => element.value).toList(); + + if (!value.contains(clientId)) { + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'x509_san_dns scheme error', + }, + ); + } + + final publicKey = cert.publicKey; + if (publicKey is x509.RsaPublicKey) { + final BigInt modulus = BigInt.parse(publicKey.modulus.toString()); + final n = base64Encode(modulus.toBytes); + final publicKeyJwk = { + 'e': 'AQAB', + 'kty': 'RSA', + 'n': n.replaceAll('=', ''), + }; + return publicKeyJwk; + } + } + return null; +} diff --git a/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart b/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart index 04dcfe6e3..8f01cc428 100644 --- a/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart +++ b/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart @@ -13,11 +13,9 @@ import 'package:altme/polygon_id/polygon_id.dart'; import 'package:altme/query_by_example/query_by_example.dart'; import 'package:altme/scan/scan.dart'; import 'package:altme/wallet/cubit/wallet_cubit.dart'; -import 'package:asn1lib/asn1lib.dart' as asn1lib; import 'package:beacon_flutter/beacon_flutter.dart'; import 'package:bloc/bloc.dart'; import 'package:credential_manifest/credential_manifest.dart'; -import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'; import 'package:did_kit/did_kit.dart'; import 'package:dio/dio.dart'; import 'package:equatable/equatable.dart'; @@ -25,9 +23,7 @@ import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:jwt_decode/jwt_decode.dart'; import 'package:oidc4vc/oidc4vc.dart'; -import 'package:pointycastle/pointycastle.dart' as pc; import 'package:secure_storage/secure_storage.dart'; -import 'package:x509/x509.dart' as x509; part 'qr_code_scan_cubit.g.dart'; part 'qr_code_scan_state.dart'; @@ -1094,8 +1090,11 @@ class QRCodeScanCubit extends Cubit { if (clientIdScheme != null) { if (clientIdScheme == 'x509_san_dns') { - publicKeyJwk = - await checkX509(clientId: clientId, encodedData: encodedData); + publicKeyJwk = await checkX509( + clientId: clientId, + encodedData: encodedData, + jwtDecode: jwtDecode, + ); } } @@ -1127,89 +1126,6 @@ class QRCodeScanCubit extends Cubit { } } - Future?> checkX509({ - required String encodedData, - required String clientId, - }) async { - final Map header = - decodeHeader(jwtDecode: jwtDecode, token: encodedData); - - final x5c = header['x5c']; - - if (x5c != null) { - if (x5c is! List) { - throw ResponseMessage( - data: { - 'error': 'invalid_format', - 'error_description': 'x509_san_dns scheme error', - }, - ); - } - - //array x5c[0], it is a certificat in DER format (binary) - final certificate = x5c.firstOrNull; - - if (certificate == null) { - throw ResponseMessage( - data: { - 'error': 'invalid_format', - 'error_description': 'x509_san_dns scheme error', - }, - ); - } - - final decoded = base64Decode(certificate.toString()); - final seq = asn1lib.ASN1Sequence.fromBytes(decoded); - final cert = x509.X509Certificate.fromAsn1(seq); - - final subject = cert.tbsCertificate.subject; - - if (subject == null) { - throw ResponseMessage( - data: { - 'error': 'invalid_format', - 'error_description': 'x509_san_dns scheme error', - }, - ); - } - - final names = subject.names; - - if (names.isEmpty) { - throw ResponseMessage( - data: { - 'error': 'invalid_format', - 'error_description': 'x509_san_dns scheme error', - }, - ); - } - - final value = names[0].entries.map((element) => element.value).toList(); - - if (!value.contains(clientId)) { - throw ResponseMessage( - data: { - 'error': 'invalid_format', - 'error_description': 'x509_san_dns scheme error', - }, - ); - } - - final publicKey = cert.publicKey; - if (publicKey is x509.RsaPublicKey) { - final BigInt modulus = BigInt.parse(publicKey.modulus.toString()); - final n = base64Encode(modulus.toBytes); - final publicKeyJwk = { - 'e': 'AQAB', - 'kty': 'RSA', - 'n': n.replaceAll('=', ''), - }; - return publicKeyJwk; - } - } - return null; - } - /// complete SIOPV2 Flow Future completeSiopV2Flow() async { try {