From c3e1f0a8fdbf4252266974237b45fb4959151e84 Mon Sep 17 00:00:00 2001 From: Bibash Shrestha Date: Mon, 27 May 2024 16:41:26 +0545 Subject: [PATCH 1/8] fix: Solve selective disclosure error during presentation #2681 --- .../selective_disclosure_pick_cubit.dart | 22 ++++++++++++------- .../view/selective_disclosure_pick_page.dart | 13 +++++++++-- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart index 3c1f5fca8..408ed9b32 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:altme/app/shared/shared.dart'; import 'package:altme/dashboard/home/home.dart'; import 'package:altme/selective_disclosure/selective_disclosure.dart'; @@ -81,20 +83,24 @@ class SelectiveDisclosureCubit extends Cubit { int? index; if (threeDotValue != null) { - for (final element - in selectiveDisclosure.disclosureToContent.entries.toList()) { + final disclosureToContentEntries = + selectiveDisclosure.disclosureToContent.entries.toList(); + + for (final element in disclosureToContentEntries) { final sh256Hash = oidc4vc.sh256HashOfContent(element.value.toString()); + if (sh256Hash == threeDotValue) { final disclosure = element.key.replaceAll('=', ''); - - index = selectiveDisclosure.disclosureFromJWT - .indexWhere((element) => element == disclosure); + index = disclosureToContentEntries + .indexWhere((entry) => entry.key == disclosure); + break; } } } else if (claimsKey != null) { - index = selectiveDisclosure.extractedValuesFromJwt.entries - .toList() - .indexWhere((entry) => entry.key == claimsKey); + index = + selectiveDisclosure.disclosureToContent.entries.toList().indexWhere( + (entry) => entry.value.toString().contains(claimsKey), + ); } if (index == null) { diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart index bd868cc05..86ae182e0 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart @@ -210,6 +210,9 @@ class _SelectiveDisclosurePickViewState } } + final selectiveDisclosure = + SelectiveDisclosure(widget.credentialToBePresented); + final encryptedValues = widget.credentialToBePresented.jwt ?.split('~') .where((element) => element.isNotEmpty) @@ -218,10 +221,16 @@ class _SelectiveDisclosurePickViewState if (encryptedValues != null) { var newJwt = '${encryptedValues[0]}~'; - encryptedValues.removeAt(0); + // encryptedValues.removeAt(0); + + final organizedDisclosure = selectiveDisclosure + .disclosureToContent.entries + .toList() + .map((element) => element.key) + .toList(); for (final index in selectedSDIndexInJWT) { - newJwt = '$newJwt${encryptedValues[index]}~'; + newJwt = '$newJwt${organizedDisclosure[index]}~'; } // Key Binding JWT From 3cef1b4717ef1deaa1495e0a7bf66bb6476369e5 Mon Sep 17 00:00:00 2001 From: hawkbee1 Date: Tue, 28 May 2024 06:40:59 +0000 Subject: [PATCH 2/8] Make subject_syntax_types_supported from client metadata optional --- .../cubit/qr_code_scan_cubit.dart | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) 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 ee2a864c7..dd2731a86 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 @@ -632,16 +632,19 @@ class QRCodeScanCubit extends Cubit { if (clientMetadata != null) { final clientMetadataMap = jsonDecode(clientMetadata) as Map; - final data = - clientMetadataMap['subject_syntax_types_supported'] as List; - if (!data.contains('did:key')) { - if (isSecurityHigh) { - throw ResponseMessage( - data: { - 'error': 'unsupported_response_type', - 'error_description': 'The subject syntax type is not supported.', - }, - ); + if (clientMetadataMap['subject_syntax_types_supported'] != null) { + final data = clientMetadataMap['subject_syntax_types_supported'] + as List; + if (!data.contains('did:key')) { + if (isSecurityHigh) { + throw ResponseMessage( + data: { + 'error': 'unsupported_response_type', + 'error_description': + 'The subject syntax type is not supported.', + }, + ); + } } } } From 0d5c71d85e43efce548f014f43946e677dbe171e Mon Sep 17 00:00:00 2001 From: hawkbee1 Date: Wed, 29 May 2024 08:03:07 +0000 Subject: [PATCH 3/8] remove duplicates from getCredentialsFromFilterList --- .../helpers/get_credentials_from_filter_list.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_filter_list.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_filter_list.dart index e06a2ca07..44440bafc 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_filter_list.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_filter_list.dart @@ -63,7 +63,7 @@ List getCredentialsFromFilterList({ } } } - return selectedCredential; + return selectedCredential.toSet().toList(); } return credentialList; } From ef9e2807b79f5cb7bd2b245cf6941c68c3a4289b Mon Sep 17 00:00:00 2001 From: hawkbee1 Date: Wed, 29 May 2024 08:10:56 +0000 Subject: [PATCH 4/8] Revert "As a proofPurpose, use assertionMethod instead of authentication except for DidAuth" This reverts commit c25f9a73aa1eda6ede2a44902269547b336d896b. --- lib/scan/cubit/scan_cubit.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/scan/cubit/scan_cubit.dart b/lib/scan/cubit/scan_cubit.dart index 4a93e0f7e..c45b3ccc8 100644 --- a/lib/scan/cubit/scan_cubit.dart +++ b/lib/scan/cubit/scan_cubit.dart @@ -359,7 +359,7 @@ class ScanCubit extends Cubit { /// proof check to fail because of time difference on server final options = jsonEncode({ 'verificationMethod': kid, - 'proofPurpose': 'assertionMethod', + 'proofPurpose': 'authentication', 'challenge': challenge, 'domain': domain, 'created': DateTime.now() @@ -894,7 +894,7 @@ class ScanCubit extends Cubit { /// proof check to fail because of time difference on server final options = jsonEncode({ 'verificationMethod': kid, - 'proofPurpose': 'assertionMethod', + 'proofPurpose': 'authentication', 'challenge': nonce, 'domain': clientId, 'created': DateTime.now() From b3972bee436e69189c10541e56044b022f1c8fea Mon Sep 17 00:00:00 2001 From: hawkbee1 Date: Wed, 29 May 2024 10:15:51 +0000 Subject: [PATCH 5/8] Prevent useless rebuild of whole credential_manifest_credential_offer_pick_page --- .../selective_disclosure_pick_cubit.dart | 2 -- .../view/selective_disclosure_pick_page.dart | 29 +++++++++------- .../inject_selective_disclosure_state.dart | 34 +++++++++++++++++++ 3 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 lib/selective_disclosure/widget/inject_selective_disclosure_state.dart diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart index 408ed9b32..2f6d3141d 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:altme/app/shared/shared.dart'; import 'package:altme/dashboard/home/home.dart'; import 'package:altme/selective_disclosure/selective_disclosure.dart'; diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart index 86ae182e0..e738ebf3d 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/view/selective_disclosure_pick_page.dart @@ -5,7 +5,7 @@ import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/scan/cubit/scan_cubit.dart'; import 'package:altme/selective_disclosure/selective_disclosure.dart'; -import 'package:altme/selective_disclosure/widget/display_selective_disclosure.dart'; +import 'package:altme/selective_disclosure/widget/inject_selective_disclosure_state.dart'; import 'package:credential_manifest/credential_manifest.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -115,8 +115,8 @@ class _SelectiveDisclosurePickViewState ); } }, - child: BlocBuilder( - builder: (context, state) { + child: Builder( + builder: (BuildContext context) { final profileSetting = context.read().state.model.profileSetting; @@ -143,10 +143,8 @@ class _SelectiveDisclosurePickViewState isDiscover: false, ), const SizedBox(height: 20), - DisplaySelectiveDisclosure( + ConsumeSelectiveDisclosureCubit( credentialModel: widget.credentialToBePresented, - claims: null, - selectiveDisclosureState: state, onPressed: (claimKey, claimKeyId, threeDotValue) { context.read().disclosureAction( claimsKey: claimKey, @@ -164,13 +162,18 @@ class _SelectiveDisclosurePickViewState padding: const EdgeInsets.all(16), child: Tooltip( message: l10n.credentialPickPresent, - child: MyGradientButton( - onPressed: () => present( - context: context, - selectedSDIndexInJWT: state.selectedSDIndexInJWT, - uri: widget.uri, - ), - text: l10n.credentialPickPresent, + child: BlocBuilder( + builder: (context, state) { + return MyGradientButton( + onPressed: () => present( + context: context, + selectedSDIndexInJWT: state.selectedSDIndexInJWT, + uri: widget.uri, + ), + text: l10n.credentialPickPresent, + ); + }, ), ), ), diff --git a/lib/selective_disclosure/widget/inject_selective_disclosure_state.dart b/lib/selective_disclosure/widget/inject_selective_disclosure_state.dart new file mode 100644 index 000000000..d8a040d9e --- /dev/null +++ b/lib/selective_disclosure/widget/inject_selective_disclosure_state.dart @@ -0,0 +1,34 @@ +import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/selective_disclosure/widget/display_selective_disclosure.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class ConsumeSelectiveDisclosureCubit extends StatelessWidget { + const ConsumeSelectiveDisclosureCubit({ + super.key, + required this.credentialModel, + required this.showVertically, + this.onPressed, + this.parentKeyId, + }); + + final CredentialModel credentialModel; + final bool showVertically; + final void Function(String?, String, String?)? onPressed; + + final String? parentKeyId; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return DisplaySelectiveDisclosure( + credentialModel: credentialModel, + selectiveDisclosureState: state, + onPressed: onPressed, + showVertically: true, + ); + }, + ); + } +} From aca6a282368b5590b29266f21b3f96437dd4f8b4 Mon Sep 17 00:00:00 2001 From: hawkbee1 Date: Wed, 29 May 2024 10:38:33 +0000 Subject: [PATCH 6/8] OIDC4VCI with sd-jwt, attribuite '_sd_alg' is optional #2693 --- lib/oidc4vc/add_oidc4vc_credential.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/oidc4vc/add_oidc4vc_credential.dart b/lib/oidc4vc/add_oidc4vc_credential.dart index 78ffad433..22484f55e 100644 --- a/lib/oidc4vc/add_oidc4vc_credential.dart +++ b/lib/oidc4vc/add_oidc4vc_credential.dart @@ -32,9 +32,9 @@ Future addOIDC4VCCredential({ final jsonContent = jwtDecode.parseJwt(data); if (format == VCFormatType.vcSdJWT.vcValue) { - final sdAlg = jsonContent['_sd_alg']; + final sdAlg = jsonContent['_sd_alg']??'sha-256'; - if (sdAlg == null || sdAlg != 'sha-256') { + if (sdAlg != 'sha-256') { throw ResponseMessage( data: { 'error': 'invalid_request', From 30b1637646bf7a0733b20c3ddc8e8fd0dbf6c11d Mon Sep 17 00:00:00 2001 From: hawkbee1 Date: Wed, 29 May 2024 13:12:49 +0000 Subject: [PATCH 7/8] SD-JWT presentation: If field is not optional the element is automatically selected --- .../selective_disclosure_pick_cubit.dart | 5 ++- .../widget/display_selective_disclosure.dart | 39 +++++++++++++++++-- .../lib/src/models/field.dart | 7 ++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart index 2f6d3141d..13e7e0182 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/selective_disclosure/cubit/selective_disclosure_pick_cubit.dart @@ -42,7 +42,10 @@ class SelectiveDisclosureCubit extends Cubit { final searchList = getTextsFromCredential(path, credentialData); for (final element in searchList) { final key = path.split('.').toList().last; - json[key] = element; + json[key] = { + 'element': element, + 'optional': field.optional, + }; } } } diff --git a/lib/selective_disclosure/widget/display_selective_disclosure.dart b/lib/selective_disclosure/widget/display_selective_disclosure.dart index eed3b9ee0..5757ef2fc 100644 --- a/lib/selective_disclosure/widget/display_selective_disclosure.dart +++ b/lib/selective_disclosure/widget/display_selective_disclosure.dart @@ -129,6 +129,7 @@ class DisplaySelectiveDisclosure extends StatelessWidget { } bool? disable; + bool selected = false; if (selectiveDisclosureState != null) { final limitDisclosure = @@ -143,12 +144,42 @@ class DisplaySelectiveDisclosure extends StatelessWidget { filters.forEach((key, value) { if (claims.threeDotValue != null) { if (claimKey.contains(key) && - claims.data.replaceAll(' ', '') == value) { + claims.data.replaceAll(' ', '') == + value['element']) { disable = false; + selected = value['optional'] as bool; + if (!selectiveDisclosureState! + .selectedClaimsKeyIds + .contains(keyToCheck) && + selected == true) { + context + .read() + .disclosureAction( + claimsKey: key, + credentialModel: credentialModel, + threeDotValue: claims.threeDotValue, + claimKeyId: claimKey, + ); + } } } else { - if (claimKey == key && claims.data == value) { + if (claimKey == key && + claims.data == value['element']) { disable = false; + selected = !(value['optional'] as bool); + if (!selectiveDisclosureState! + .selectedClaimsKeyIds + .contains(keyToCheck) && + selected == true) { + context + .read() + .disclosureAction( + claimsKey: key, + credentialModel: credentialModel, + threeDotValue: claims.threeDotValue, + claimKeyId: claimKey, + ); + } } } }); @@ -159,7 +190,9 @@ class DisplaySelectiveDisclosure extends StatelessWidget { return TransparentInkWell( onTap: () { - if (disable != null && disable!) return; + if ((disable != null && disable!) || selected == true) { + return; + } onPressed?.call(key, claimKey, claims.threeDotValue); }, diff --git a/packages/credential_manifest/lib/src/models/field.dart b/packages/credential_manifest/lib/src/models/field.dart index e3693663a..1aa487a0f 100644 --- a/packages/credential_manifest/lib/src/models/field.dart +++ b/packages/credential_manifest/lib/src/models/field.dart @@ -15,6 +15,13 @@ class Field { final List path; final Filter? filter; + + // The fields object MAY contain an optional property. + // The value of this property MUST be a boolean, wherein true indicates + // the field is optional, and false or non-presence of the property indicates + // the field is required. Even when the optional property is present, + // the value located at the indicated path of the field MUST validate against + // the JSON Schema filter, if a filter is present. @JsonKey(defaultValue: false) final bool optional; From f63c1022591e3f7389e36860178ef8cb22099d99 Mon Sep 17 00:00:00 2001 From: hawkbee1 Date: Wed, 29 May 2024 15:21:30 +0000 Subject: [PATCH 8/8] Verifier MEECO : wallet fails #2674 --- .../cubit/qr_code_scan_cubit.dart | 20 ------------------- 1 file changed, 20 deletions(-) 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 dd2731a86..e9df2d911 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 @@ -628,26 +628,6 @@ class QRCodeScanCubit extends Cubit { } } - final clientMetadata = state.uri!.queryParameters['client_metadata']; - if (clientMetadata != null) { - final clientMetadataMap = - jsonDecode(clientMetadata) as Map; - if (clientMetadataMap['subject_syntax_types_supported'] != null) { - final data = clientMetadataMap['subject_syntax_types_supported'] - as List; - if (!data.contains('did:key')) { - if (isSecurityHigh) { - throw ResponseMessage( - data: { - 'error': 'unsupported_response_type', - 'error_description': - 'The subject syntax type is not supported.', - }, - ); - } - } - } - } final redirectUri = state.uri!.queryParameters['redirect_uri']; final responseUri = state.uri!.queryParameters['response_uri'];