Skip to content

Commit

Permalink
Merge pull request #2686 from TalaoDAO/sd_issue#2681
Browse files Browse the repository at this point in the history
fix: Solve selective disclosure error during presentation #2681
  • Loading branch information
hawkbee1 authored May 29, 2024
2 parents 35f0832 + 783a9cc commit dded9e3
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ List<CredentialModel> getCredentialsFromFilterList({
}
}
}
return selectedCredential;
return selectedCredential.toSet().toList();
}
return credentialList;
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ class SelectiveDisclosureCubit extends Cubit<SelectiveDisclosureState> {
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,
};
}
}
}
Expand Down Expand Up @@ -81,20 +84,24 @@ class SelectiveDisclosureCubit extends Cubit<SelectiveDisclosureState> {
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -115,8 +115,8 @@ class _SelectiveDisclosurePickViewState
);
}
},
child: BlocBuilder<SelectiveDisclosureCubit, SelectiveDisclosureState>(
builder: (context, state) {
child: Builder(
builder: (BuildContext context) {
final profileSetting =
context.read<ProfileCubit>().state.model.profileSetting;

Expand All @@ -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<SelectiveDisclosureCubit>().disclosureAction(
claimsKey: claimKey,
Expand All @@ -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<SelectiveDisclosureCubit,
SelectiveDisclosureState>(
builder: (context, state) {
return MyGradientButton(
onPressed: () => present(
context: context,
selectedSDIndexInJWT: state.selectedSDIndexInJWT,
uri: widget.uri,
),
text: l10n.credentialPickPresent,
);
},
),
),
),
Expand Down Expand Up @@ -210,6 +213,9 @@ class _SelectiveDisclosurePickViewState
}
}

final selectiveDisclosure =
SelectiveDisclosure(widget.credentialToBePresented);

final encryptedValues = widget.credentialToBePresented.jwt
?.split('~')
.where((element) => element.isNotEmpty)
Expand All @@ -218,10 +224,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
Expand Down
23 changes: 0 additions & 23 deletions lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -627,29 +627,6 @@ class QRCodeScanCubit extends Cubit<QRCodeScanState> {
}
}
}
final clientType = profileCubit.state.model.profileSetting
.selfSovereignIdentityOptions.customOidc4vcProfile.clientType;

if (clientType != ClientType.p256JWKThumprint) {
final clientMetadata = state.uri!.queryParameters['client_metadata'];
if (clientMetadata != null) {
final clientMetadataMap =
jsonDecode(clientMetadata) as Map<String, dynamic>;
final data = clientMetadataMap['subject_syntax_types_supported']
as List<dynamic>;
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'];
Expand Down
4 changes: 2 additions & 2 deletions lib/oidc4vc/add_oidc4vc_credential.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ Future<void> 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',
Expand Down
4 changes: 2 additions & 2 deletions lib/scan/cubit/scan_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ class ScanCubit extends Cubit<ScanState> {
/// 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()
Expand Down Expand Up @@ -899,7 +899,7 @@ class ScanCubit extends Cubit<ScanState> {
/// 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()
Expand Down
39 changes: 36 additions & 3 deletions lib/selective_disclosure/widget/display_selective_disclosure.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class DisplaySelectiveDisclosure extends StatelessWidget {
}

bool? disable;
bool selected = false;

if (selectiveDisclosureState != null) {
final limitDisclosure =
Expand All @@ -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<SelectiveDisclosureCubit>()
.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<SelectiveDisclosureCubit>()
.disclosureAction(
claimsKey: key,
credentialModel: credentialModel,
threeDotValue: claims.threeDotValue,
claimKeyId: claimKey,
);
}
}
}
});
Expand All @@ -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);
},
Expand Down
Original file line number Diff line number Diff line change
@@ -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<SelectiveDisclosureCubit, SelectiveDisclosureState>(
builder: (context, state) {
return DisplaySelectiveDisclosure(
credentialModel: credentialModel,
selectiveDisclosureState: state,
onPressed: onPressed,
showVertically: true,
);
},
);
}
}
7 changes: 7 additions & 0 deletions packages/credential_manifest/lib/src/models/field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ class Field {

final List<String> 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;

Expand Down

0 comments on commit dded9e3

Please sign in to comment.