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 9253bdd5e..52d8231a5 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,9 +13,11 @@ 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'; @@ -23,8 +25,9 @@ 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'; @@ -1059,32 +1062,55 @@ class QRCodeScanCubit extends Cubit { .selfSovereignIdentityOptions.customOidc4vcProfile; final isSecurityEnabled = customOidc4vcProfile.securityLevel; - final enableJWKThumbprint = - customOidc4vcProfile.clientType == ClientType.jwkThumbprint; - if (isSecurityEnabled && enableJWKThumbprint) { + if (isSecurityEnabled) { final Map payload = decodePayload(jwtDecode: jwtDecode, token: encodedData as String); - final String issuer = payload['client_id'].toString(); + final String clientId = payload['client_id'].toString(); //check Signature try { + /// client_id_scheme = did, you need tio use the universal resolver + /// + /// client_id_scheme = redirect_uri + /// (default case if no client_id_scheme)c you need to use the jwks + /// + /// client_id_scheme = x509_san_dns, you need the key from the + /// certificate + /// + /// client_id_scheme = verifier_attestation, the key will be in a + /// jwt inside teh header + /// + /// if client_id starts with "did:" lets consider it is + /// client_id_scheme n= did, universal resolver + /// + /// if client_id starts with http, lets consider it is + /// client_id_scheme = redirect_uri, fetch jwks + + final clientIdScheme = payload['client_id_scheme']; + + if (clientIdScheme != null) { + if (clientIdScheme == 'x509_san_dns') { + await checkX509(clientId: clientId, encodedData: encodedData); + } + } + final VerificationType isVerified = await verifyEncodedData( - issuer: issuer, + issuer: clientId, jwtDecode: jwtDecode, jwt: encodedData, ); - if (isVerified == VerificationType.verified) { - emit(state.acceptHost()); - } else { - emitError( + if (isVerified != VerificationType.verified) { + return emitError( ResponseMessage( message: ResponseString.RESPONSE_STRING_invalidRequest, ), ); } + + emit(state.acceptHost()); } catch (e) { emitError( ResponseMessage( @@ -1097,6 +1123,82 @@ 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; + + // final publicKey = cert.publicKey as x509.PublicKey; + // if (publicKey is x509.RsaPublicKey) { + // print(publicKey.modulus.toString()); + // print(publicKey.exponent); + // } + + 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', + }, + ); + } + } + } + /// complete SIOPV2 Flow Future completeSiopV2Flow() async { try {