Skip to content

Commit

Permalink
feat: Support JWT-KB #2502
Browse files Browse the repository at this point in the history
  • Loading branch information
bibash28 committed Mar 18, 2024
1 parent 9315e68 commit 620c630
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 6 deletions.
18 changes: 18 additions & 0 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
336B5EA3C3EB96365DF03060 /* [CP] Embed Pods Frameworks */,
BCBC5F3194C8151A0253FE19 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
Expand Down Expand Up @@ -282,6 +283,23 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
BCBC5F3194C8151A0253FE19 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
Expand Down
9 changes: 8 additions & 1 deletion lib/app/shared/helper_functions/helper_functions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:altme/oidc4vc/oidc4vc.dart';
import 'package:altme/selective_disclosure/selective_disclosure.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';

import 'package:dartez/dartez.dart';
import 'package:device_info_plus/device_info_plus.dart';
Expand Down Expand Up @@ -1508,7 +1509,7 @@ Future<(String?, String?, String?, String?)> getClientDetails({
final jwtProofOfPossession = profileCubit.oidc4vc.generateToken(
payload: payload,
tokenParameters: tokenParameters,
clientSecretJwt: true,
ignoreProofHeaderType: true,
);

clientAssertion = '$walletAttestationData~$jwtProofOfPossession';
Expand Down Expand Up @@ -1681,3 +1682,9 @@ List<String> getStringCredentialsForToken({

return credentialList;
}

String hash(String text) {
final bytes = utf8.encode(text);
final digest = sha256.convert(bytes);
return base64Url.encode(digest.bytes).replaceAll('=', '');
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import 'dart:convert';

import 'package:altme/app/app.dart';
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/widget/display_selective_disclosure.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:oidc4vc/oidc4vc.dart';

class SelectiveDisclosurePickPage extends StatelessWidget {
const SelectiveDisclosurePickPage({
Expand Down Expand Up @@ -112,7 +115,7 @@ class SelectiveDisclosurePickView extends StatelessWidget {
: () => present(
context: context,
selectedIndex: state.selected,
skip: false,
uri: uri,
),
text: l10n.credentialPickPresent,
),
Expand All @@ -128,7 +131,7 @@ class SelectiveDisclosurePickView extends StatelessWidget {
Future<void> present({
required BuildContext context,
required List<int> selectedIndex,
required bool skip,
required Uri uri,
}) async {
final bool userPINCodeForAuthentication = context
.read<ProfileCubit>()
Expand Down Expand Up @@ -165,6 +168,52 @@ class SelectiveDisclosurePickView extends StatelessWidget {
newJwt = '$newJwt${encryptedValues[index]}~';
}

// Key Binding JWT

final profileCubit = context.read<ProfileCubit>();

final customOidc4vcProfile = profileCubit.state.model.profileSetting
.selfSovereignIdentityOptions.customOidc4vcProfile;

final didKeyType = customOidc4vcProfile.defaultDid;

final privateKey = await fetchPrivateKey(
profileCubit: profileCubit,
didKeyType: didKeyType,
);

final tokenParameters = TokenParameters(
privateKey: jsonDecode(privateKey) as Map<String, dynamic>,
did: '', // just added as it is required field
mediaType: MediaType.selectiveDisclosure,
clientType:
ClientType.jwkThumbprint, // just added as it is required field
proofHeaderType: customOidc4vcProfile.proofHeader,
clientId: '', // just added as it is required field
);

final iat = (DateTime.now().millisecondsSinceEpoch / 1000).round();
final sdHash = hash(newJwt);

final nonce = uri.queryParameters['nonce'] ?? '';
final clientId = uri.queryParameters['client_id'] ?? '';

final payload = {
'nonce': nonce,
'aud': clientId,
'iat': iat,
'sd_hash': sdHash,
};

/// sign and get token
final jwtToken = profileCubit.oidc4vc.generateToken(
payload: payload,
tokenParameters: tokenParameters,
ignoreProofHeaderType: true,
);

newJwt = '$newJwt$jwtToken';

final CredentialModel newModel =
credentialToBePresented.copyWith(selectiveDisclosureJwt: newJwt);

Expand Down
12 changes: 11 additions & 1 deletion lib/scan/cubit/scan_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -909,7 +909,7 @@ class ScanCubit extends Cubit<ScanState> {
privateKey,
);
return vpToken;
} else if (presentJwtVc || presentJwtVcJson || presentVcSdJwt) {
} else if (presentJwtVc || presentJwtVcJson) {
final credentialList = getStringCredentialsForToken(
credentialsToBePresented: credentialsToBePresented,
profileCubit: profileCubit,
Expand All @@ -925,6 +925,16 @@ class ScanCubit extends Cubit<ScanState> {
proofHeaderType: customOidc4vcProfile.proofHeader,
);

return vpToken;
} else if (presentVcSdJwt) {
final credentialList = getStringCredentialsForToken(
credentialsToBePresented: credentialsToBePresented,
profileCubit: profileCubit,
);

final vpToken = credentialList.first;
// considering only one

return vpToken;
} else {
throw Exception();
Expand Down
3 changes: 3 additions & 0 deletions packages/oidc4vc/lib/src/media_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ enum MediaType {
proofOfOwnership,
basic,
walletAttestation,
selectiveDisclosure,
}

extension MediaTypeX on MediaType {
Expand All @@ -13,6 +14,8 @@ extension MediaTypeX on MediaType {
return 'JWT';
case MediaType.walletAttestation:
return 'wiar+jwt';
case MediaType.selectiveDisclosure:
return 'kb+jwt';
}
}
}
5 changes: 3 additions & 2 deletions packages/oidc4vc/lib/src/oidc4vc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1409,7 +1409,7 @@ class OIDC4VC {
String generateToken({
required Map<String, dynamic> payload,
required TokenParameters tokenParameters,
bool clientSecretJwt = false,
bool ignoreProofHeaderType = false,
}) {
final kty = tokenParameters.privateKey['kty'].toString();

Expand Down Expand Up @@ -1441,8 +1441,9 @@ class OIDC4VC {
// add a key to sign, can only add one for JWT
..addRecipient(key, algorithm: tokenParameters.alg);

if (!clientSecretJwt) {
if (!ignoreProofHeaderType) {
/// Proof Header Type is ignored for clientSecretJwt
// also ignored for KB jwt
vpBuilder.setProtectedHeader('typ', tokenParameters.mediaType.typ);

switch (tokenParameters.proofHeaderType) {
Expand Down

0 comments on commit 620c630

Please sign in to comment.