From b917ab3ea2ec53ba128d6efb4a46dc3e4f412930 Mon Sep 17 00:00:00 2001 From: Bibash Shrestha Date: Mon, 8 Apr 2024 19:35:53 +0545 Subject: [PATCH] feat: Manage statuslist 2021 for different formats #2255 #2570 --- .../cubit/credential_details_cubit.dart | 84 ++++++++++++++++++- .../models/credential/credential.dart | 2 +- lib/dashboard/profile/models/profile.dart | 4 +- packages/oidc4vc/lib/src/oidc4vc.dart | 15 +++- 4 files changed, 98 insertions(+), 7 deletions(-) diff --git a/lib/dashboard/home/tab_bar/credentials/detail/cubit/credential_details_cubit.dart b/lib/dashboard/home/tab_bar/credentials/detail/cubit/credential_details_cubit.dart index 195104a3d..c39040fc5 100644 --- a/lib/dashboard/home/tab_bar/credentials/detail/cubit/credential_details_cubit.dart +++ b/lib/dashboard/home/tab_bar/credentials/detail/cubit/credential_details_cubit.dart @@ -123,10 +123,11 @@ class CredentialDetailsCubit extends Cubit { 'accept': 'application/statuslist+jwt', }, ); + final payload = jwtDecode.parseJwt(response.toString()); /// verify the signature of the VC with the kid of the JWT final VerificationType isVerified = await verifyEncodedData( - issuer: item.issuer, + issuer: payload['iss']?.toString() ?? item.issuer, jwtDecode: jwtDecode, jwt: response.toString(), ); @@ -142,7 +143,6 @@ class CredentialDetailsCubit extends Cubit { return; } - final payload = jwtDecode.parseJwt(response.toString()); final newStatusList = payload['status_list']; if (newStatusList != null && newStatusList is Map) { @@ -155,7 +155,7 @@ class CredentialDetailsCubit extends Cubit { profileCubit.oidc4vc.decodeAndZlibDecompress(lst); final byteToCheck = decompressedBytes[bytes]; - final posOfBit = profileCubit.oidc4vc.getPositionOfBit(idx); + final posOfBit = profileCubit.oidc4vc.getPositionOfZlibBit(idx); final bit = profileCubit.oidc4vc .getBit(byte: byteToCheck, bitPosition: posOfBit); @@ -177,6 +177,84 @@ class CredentialDetailsCubit extends Cubit { } } + if (item.format == VCFormatType.jwtVc.value || + item.format == VCFormatType.jwtVcJson.value || + item.format == VCFormatType.ldpVc.value) { + final credentialStatus = item.credentialPreview.credentialStatus; + if (credentialStatus != null) { + if (credentialStatus is List) { + for (final iteratedData in credentialStatus) { + if (iteratedData is Map) { + final data = CredentialStatusField.fromJson(iteratedData); + + final dynamic response = await client.get( + data.statusListCredential, + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + 'accept': 'application/statuslist+jwt', + }, + ); + + final payload = jwtDecode.parseJwt(response.toString()); + + // verify the signature of the VC with the kid of the JWT + final VerificationType isVerified = await verifyEncodedData( + issuer: payload['iss']?.toString() ?? item.issuer, + jwtDecode: jwtDecode, + jwt: response.toString(), + ); + + if (isVerified != VerificationType.verified) { + emit( + state.copyWith( + credentialStatus: + CredentialStatus.statusListInvalidSignature, + status: AppStatus.idle, + ), + ); + return; + } + + final vc = payload['vc']; + if (vc != null && vc is Map) { + final credentialSubject = vc['credentialSubject']; + if (credentialSubject != null && + credentialSubject is Map) { + final encodedList = credentialSubject['encodedList']; + + if (encodedList != null && encodedList is String) { + final decompressedBytes = profileCubit.oidc4vc + .decodeAndGzibDecompress(encodedList); + + final idx = int.parse(data.statusListIndex); + final bytes = profileCubit.oidc4vc.getByte(idx); + final byteToCheck = decompressedBytes[bytes]; + final posOfBit = + profileCubit.oidc4vc.getPositionOfGZipBit(idx); + final bit = profileCubit.oidc4vc + .getBit(byte: byteToCheck, bitPosition: posOfBit); + + if (bit == 0) { + // active + } else { + // revoked + emit( + state.copyWith( + credentialStatus: CredentialStatus.invalidStatus, + status: AppStatus.idle, + ), + ); + return; + } + } + } + } + } + } + } + } + } + if (item.jwt != null) { final jwt = item.jwt!; final Map payload = jwtDecode.parseJwt(jwt); diff --git a/lib/dashboard/home/tab_bar/credentials/models/credential/credential.dart b/lib/dashboard/home/tab_bar/credentials/models/credential/credential.dart index d4f3c8f9b..79db2e76f 100644 --- a/lib/dashboard/home/tab_bar/credentials/models/credential/credential.dart +++ b/lib/dashboard/home/tab_bar/credentials/models/credential/credential.dart @@ -85,7 +85,7 @@ class Credential { CredentialSubjectModel? credentialSubjectModel, List? description, List? name, - CredentialStatusField? credentialStatus, + dynamic credentialStatus, List? evidence, }) { return Credential( diff --git a/lib/dashboard/profile/models/profile.dart b/lib/dashboard/profile/models/profile.dart index c9ce6b788..fbac64c53 100644 --- a/lib/dashboard/profile/models/profile.dart +++ b/lib/dashboard/profile/models/profile.dart @@ -61,7 +61,7 @@ class ProfileModel extends Equatable { oidc4vciDraft: OIDC4VCIDraftType.draft11, oidc4vpDraft: OIDC4VPDraftType.draft10, scope: false, - securityLevel: false, + securityLevel: true, proofHeader: ProofHeaderType.kid, siopv2Draft: SIOPV2DraftType.draft12, clientType: ClientType.did, @@ -177,7 +177,7 @@ class ProfileModel extends Equatable { oidc4vciDraft: OIDC4VCIDraftType.draft13, oidc4vpDraft: OIDC4VPDraftType.draft10, scope: false, - securityLevel: false, + securityLevel: true, proofHeader: ProofHeaderType.kid, siopv2Draft: SIOPV2DraftType.draft12, clientType: ClientType.did, diff --git a/packages/oidc4vc/lib/src/oidc4vc.dart b/packages/oidc4vc/lib/src/oidc4vc.dart index 39aba91eb..7da9eaf52 100644 --- a/packages/oidc4vc/lib/src/oidc4vc.dart +++ b/packages/oidc4vc/lib/src/oidc4vc.dart @@ -1119,6 +1119,7 @@ class OIDC4VC { } late final bool isVerified; + if (kty == 'OKP') { isVerified = verifyTokenEdDSA( publicKey: publicKeyJwk, @@ -1621,7 +1622,9 @@ class OIDC4VC { return hash; } - int getPositionOfBit(int index) => index % 8; + int getPositionOfZlibBit(int index) => index % 8; + + int getPositionOfGZipBit(int index) => 7 - (index % 8); int getByte(int index) => index ~/ 8; @@ -1645,4 +1648,14 @@ class OIDC4VC { return decompressedBytes; } + + List decodeAndGzibDecompress(String lst) { + final paddedBase64 = lst.padRight((lst.length + 3) & ~3, '='); + final compressedBytes = base64Url.decode(paddedBase64); + + final gzib = GZipCodec(); + final decompressedBytes = gzib.decode(compressedBytes); + + return decompressedBytes; + } }