[INJIMOB-2160] use pixelpass for processing mmdoc data for rendering (#1660)

* [INJIMOB-2160] use pixelpass for processing mmdoc data for rendering

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] fix history not showing properly post download

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] add processing VC logic for iOS

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] modify selectCredential in VCItemSelectors to return selectCredential

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] refactor - remove debug logs

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] refactor - mark prop credentialWrapper as mandatory

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] refactor - optimize imports

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] refactor - remove unused functions / code block

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] refactor - format code

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] modify pixelpass module to get toJSON api from pixelpass class

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] refactor - optimize imports

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] show keytype for mso_mdoc format VCs

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] use id in mso_mdoc VC as unique VC ID

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] refactor getDisplayId method

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] update ci-client & pixelpass version

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] add runtime asset to gitignore

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] remove unused var

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-2160] bypass verification for mock VCs

This is done since mock VCs are not verifiable as of now.

Co-Authored by: BalachandarG <balachandar.g@thoughtworks.com>
Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

---------

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>
This commit is contained in:
KiruthikaJeyashankar
2024-11-06 18:55:27 +05:30
committed by GitHub
parent 1207ae81ec
commit a6ed9031f5
27 changed files with 338 additions and 153 deletions

3
.gitignore vendored
View File

@@ -133,3 +133,6 @@ injitest/target/
## Unit test
.jest/cache
## runtime bundles / assets
android/app/src/main/assets/faceModel.tflite

View File

@@ -265,7 +265,7 @@ dependencies {
implementation("io.mosip:inji-openID4VP:0.1.0-SNAPSHOT")
implementation("com.facebook.react:react-android")
implementation 'com.facebook.soloader:soloader:0.10.1+'
implementation("io.mosip:pixelpass:0.2.1")
implementation("io.mosip:pixelpass-aar:0.6.0-SNAPSHOT")
implementation("io.mosip:secure-keystore:0.3.0-SNAPSHOT")
implementation("io.mosip:tuvali:0.5.1")
implementation("io.mosip:inji-vci-client:0.2.0-SNAPSHOT")

View File

@@ -1,16 +1,19 @@
package io.mosip.residentapp;
import io.mosip.pixelpass.PixelPass;
import io.mosip.pixelpass.cbor.Utils;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;
public class RNPixelpassModule extends ReactContextBaseJavaModule {
private PixelPass pixelPass;
private final PixelPass pixelPass;
public RNPixelpassModule(ReactApplicationContext reactContext) {
super(reactContext);
pixelPass = new PixelPass();
pixelPass = new PixelPass();
}
@Override
@@ -31,10 +34,20 @@ public class RNPixelpassModule extends ReactContextBaseJavaModule {
@ReactMethod
public void generateQRData(String data, String header, Promise promise) {
try {
String qrData=pixelPass.generateQRData(data,header);
String qrData = pixelPass.generateQRData(data, header);
promise.resolve(qrData);
} catch (Exception e) {
promise.reject("ERROR_GENERATING_QR", "Failed to generate QR Data: " + e.toString());
promise.reject("ERROR_GENERATING_QR", "Failed to generate QR Data: " + e);
}
}
@ReactMethod
public void decodeBase64UrlEncodedCBORData(String data, Promise promise) {
try {
Object decodedData = pixelPass.toJson(data);
promise.resolve(decodedData.toString());
} catch (Exception e) {
promise.reject("ERROR_DECODING_DATA", "Failed to decode Data: " + e);
}
}
}

View File

@@ -32,8 +32,9 @@ export const QrCodeOverlay: React.FC<QrCodeOverlayProps> = props => {
throw new Error('No key data found');
}
} catch {
const {credential} = props.verifiableCredential;
qrData = await RNPixelpassModule.generateQRData(
JSON.stringify(props.verifiableCredential),
JSON.stringify(credential),
'',
);
await RNSecureKeystoreModule.storeData(props.meta.id, qrData);

View File

@@ -5,8 +5,8 @@ import {
selectGeneratedOn,
selectKebabPopUp,
selectWalletBindingResponse,
selectCredential,
selectVerifiableCredentialData,
selectCredential,
} from '../../machines/VerifiableCredential/VCItemMachine/VCItemSelectors';
import {useInterpret, useSelector} from '@xstate/react';
import {VCItemProps} from './Views/VCCardView';

View File

@@ -14,6 +14,7 @@ import {CARD_VIEW_DEFAULT_FIELDS, isVCLoaded} from '../common/VCUtils';
import {VCItemMachine} from '../../../machines/VerifiableCredential/VCItemMachine/VCItemMachine';
import {useTranslation} from 'react-i18next';
import {Copilot} from '../../ui/Copilot';
import {VCProcessor} from '../common/VCProcessor';
export const VCCardView: React.FC<VCItemProps> = props => {
const controller = useVcItemController(props);
@@ -30,10 +31,23 @@ export const VCCardView: React.FC<VCItemProps> = props => {
controller.UPDATE_VC_METADATA(props.vcMetadata);
}, [props.vcMetadata]);
const vc = props.isDownloading ? null : controller.credential;
const [fields, setFields] = useState([]);
const [wellknown, setWellknown] = useState(null);
const [vc, setVc] = useState(null);
useEffect(() => {
async function loadVc() {
if (!props.isDownloading) {
const processedData = await VCProcessor.processForRendering(
controller.credential,
controller.verifiableCredentialData.format,
);
setVc(processedData);
}
}
loadVc();
}, [props.isDownloading, controller.credential]);
useEffect(() => {
const {

View File

@@ -3,6 +3,7 @@ import {useTranslation} from 'react-i18next';
import {Image, ImageBackground, View} from 'react-native';
import {
Credential,
CredentialWrapper,
VerifiableCredential,
VerifiableCredentialData,
WalletBindingResponse,
@@ -40,7 +41,7 @@ const getProfileImage = (face: any) => {
};
export const VCDetailView: React.FC<VCItemDetailsProps> = props => {
const {t, i18n} = useTranslation('VcDetails');
const {t} = useTranslation('VcDetails');
const logo = props.verifiableCredentialData.issuerLogo;
const face = props.verifiableCredentialData.face;
const verifiableCredential = props.credential;
@@ -94,7 +95,9 @@ export const VCDetailView: React.FC<VCItemDetailsProps> = props => {
<Column crossAlign="center">
{getProfileImage(face)}
<QrCodeOverlay
verifiableCredential={verifiableCredential}
verifiableCredential={
props.credentialWrapper as unknown as VerifiableCredential
}
meta={props.verifiableCredentialData.vcMetadata}
/>
<Column
@@ -123,35 +126,34 @@ export const VCDetailView: React.FC<VCItemDetailsProps> = props => {
)}
</Column>
</Row>
{shouldShowHrLine(verifiableCredential) && (
<>
<View
style={[
Theme.Styles.hrLine,
{
borderBottomColor: getTextColor(
props.wellknown,
Theme.Styles.hrLine.borderBottomColor,
),
},
]}></View>
<Column padding="0 14 14 14">
{fieldItemIterator(
<>
<View
style={[
Theme.Styles.hrLine,
{
borderBottomColor: getTextColor(
props.wellknown,
Theme.Styles.hrLine.borderBottomColor,
),
},
]}></View>
<Column padding="0 14 14 14">
{shouldShowHrLine(verifiableCredential) &&
fieldItemIterator(
DETAIL_VIEW_BOTTOM_SECTION_FIELDS,
verifiableCredential,
props.wellknown,
props,
)}
<VCItemField
key={'keyTypeVcDetailView'}
fieldName={KEY_TYPE_FIELD}
fieldValue={props.keyType}
verifiableCredential={verifiableCredential}
testID={'keyTypeVcDetailView'}
/>
</Column>
</>
)}
<VCItemField
key={'keyTypeVcDetailView'}
fieldName={KEY_TYPE_FIELD}
fieldValue={props.keyType}
verifiableCredential={verifiableCredential}
testID={'keyTypeVcDetailView'}
/>
</Column>
</>
</ImageBackground>
</Column>
</Column>
@@ -246,6 +248,7 @@ export interface VCItemDetailsProps {
credential: VerifiableCredential | Credential;
verifiableCredentialData: VerifiableCredentialData;
walletBindingResponse?: WalletBindingResponse;
credentialWrapper: CredentialWrapper;
onBinding?: () => void;
activeTab?: Number;
vcHasImage: boolean;

View File

@@ -0,0 +1,26 @@
import {NativeModules} from 'react-native';
import {VerifiableCredential} from '../../../machines/VerifiableCredential/VCMetaMachine/vc';
import {VCFormat} from '../../../shared/VCFormat';
import {getVerifiableCredential} from '../../../machines/VerifiableCredential/VCItemMachine/VCItemSelectors';
import {parseJSON} from '../../../shared/Utils';
const {RNPixelpassModule} = NativeModules;
export class VCProcessor {
static async processForRendering(
vcData: VerifiableCredential,
vcFormat: String,
): Promise<any> {
if (vcFormat === VCFormat.mso_mdoc) {
if (vcData.processedCredential) {
return vcData.processedCredential;
}
const decodedString =
await RNPixelpassModule.decodeBase64UrlEncodedCBORData(
vcData.credential.toString(),
);
return parseJSON(decodedString);
}
return getVerifiableCredential(vcData);
}
}

View File

@@ -232,7 +232,7 @@ export const fieldItemIterator = (
};
export const isVCLoaded = (
verifiableCredential: Credential,
verifiableCredential: Credential | null,
fields: string[],
) => {
return verifiableCredential != null && fields.length > 0;
@@ -258,7 +258,11 @@ export const getIdType = (
wellknown: CredentialTypes | IssuerWellknownResponse,
credentialConfigurationId: string | undefined = undefined,
): string => {
if (wellknown && wellknown?.display) {
if (
wellknown &&
wellknown['credential_configurations_supported'] === undefined &&
wellknown?.display
) {
const idTypeObj = wellknown.display.map((displayProps: any) => {
return {language: displayProps.locale, value: displayProps.name};
});
@@ -266,7 +270,6 @@ export const getIdType = (
} else if (wellknown && Object.keys(wellknown).length > 0) {
let supportedCredentialsWellknown;
wellknown = parseJSON(wellknown) as unknown as Object[];
if (!!!wellknown['credential_configurations_supported']) {
return i18n.t('VcDetails:nationalCard');
}

View File

@@ -13,6 +13,8 @@
1E6875E92CA554E80086D870 /* OpenID4VP in Frameworks */ = {isa = PBXBuildFile; productRef = 1E6875E82CA554E80086D870 /* OpenID4VP */; };
1E6875EB2CA554FD0086D870 /* RNOpenID4VPModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E6875EA2CA554FD0086D870 /* RNOpenID4VPModule.m */; };
1E6875ED2CA5550F0086D870 /* RNOpenID4VPModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E6875EC2CA5550F0086D870 /* RNOpenID4VPModule.swift */; };
34873E472CD8DAF3004DE734 /* VCIClient in Frameworks */ = {isa = PBXBuildFile; productRef = 34873E462CD8DAF3004DE734 /* VCIClient */; };
34873E4D2CD8DD11004DE734 /* pixelpass in Frameworks */ = {isa = PBXBuildFile; productRef = 34873E4C2CD8DD11004DE734 /* pixelpass */; };
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
73295844242A4AD3AA52D0BE /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = D98B96A488E54CBDB286B26F /* noop-file.swift */; };
96905EF65AED1B983A6B3ABC /* libPods-Inji.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-Inji.a */; };
@@ -29,12 +31,10 @@
9C7CDF3E2C7CBEDE00243A9A /* RNSecureKeystoreModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C7CDF3D2C7CBEDE00243A9A /* RNSecureKeystoreModule.swift */; };
9C7CDF432C7CC13500243A9A /* RNSecureKeystoreModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C7CDF422C7CC13500243A9A /* RNSecureKeystoreModule.m */; };
9C7CDF492C89802C00243A9A /* securekeystore in Frameworks */ = {isa = PBXBuildFile; productRef = 9C7CDF482C89802C00243A9A /* securekeystore */; };
9CE34B1F2BFE03E4001AF414 /* pixelpass in Frameworks */ = {isa = PBXBuildFile; productRef = 9CE34B1E2BFE03E4001AF414 /* pixelpass */; };
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; };
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
E86208152C0335C5007C3E24 /* RNVCIClientModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86208142C0335C5007C3E24 /* RNVCIClientModule.swift */; };
E86208172C0335EC007C3E24 /* RNVCIClientModule.m in Sources */ = {isa = PBXBuildFile; fileRef = E86208162C0335EC007C3E24 /* RNVCIClientModule.m */; };
E8AF2B9D2C0D93E800E775F6 /* VCIClient in Frameworks */ = {isa = PBXBuildFile; productRef = E8AF2B9C2C0D93E800E775F6 /* VCIClient */; };
FD8D20B92BBAACDF009AD01C /* Fontisto.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FD8D20A62BBAACDF009AD01C /* Fontisto.ttf */; };
FD8D20BA2BBAACDF009AD01C /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FD8D20A72BBAACDF009AD01C /* Octicons.ttf */; };
FD8D20BB2BBAACDF009AD01C /* Feather.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FD8D20A82BBAACDF009AD01C /* Feather.ttf */; };
@@ -113,10 +113,10 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E8AF2B9D2C0D93E800E775F6 /* VCIClient in Frameworks */,
34873E472CD8DAF3004DE734 /* VCIClient in Frameworks */,
9C4850432C3E5873002ECBD5 /* ios-tuvali-library in Frameworks */,
1E6875E92CA554E80086D870 /* OpenID4VP in Frameworks */,
9CE34B1F2BFE03E4001AF414 /* pixelpass in Frameworks */,
34873E4D2CD8DD11004DE734 /* pixelpass in Frameworks */,
9C7CDF492C89802C00243A9A /* securekeystore in Frameworks */,
96905EF65AED1B983A6B3ABC /* libPods-Inji.a in Frameworks */,
);
@@ -281,11 +281,11 @@
);
name = Inji;
packageProductDependencies = (
9CE34B1E2BFE03E4001AF414 /* pixelpass */,
E8AF2B9C2C0D93E800E775F6 /* VCIClient */,
9C4850422C3E5873002ECBD5 /* ios-tuvali-library */,
9C7CDF482C89802C00243A9A /* securekeystore */,
1E6875E82CA554E80086D870 /* OpenID4VP */,
34873E462CD8DAF3004DE734 /* VCIClient */,
34873E4C2CD8DD11004DE734 /* pixelpass */,
);
productName = Inji;
productReference = 13B07F961A680F5B00A75B9A /* Inji.app */;
@@ -314,8 +314,8 @@
);
mainGroup = 83CBB9F61A601CBA00E9B192;
packageReferences = (
9CE34B1D2BFE03E4001AF414 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */,
E86208242C039895007C3E24 /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */,
34873E4B2CD8DD11004DE734 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */,
34873E452CD8DAF3004DE734 /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */,
9C4850412C3E5873002ECBD5 /* XCRemoteSwiftPackageReference "tuvali-ios-swift" */,
9C7CDF472C89802C00243A9A /* XCRemoteSwiftPackageReference "secure-keystore-ios-swift" */,
1E6875E72CA554E80086D870 /* XCRemoteSwiftPackageReference "inji-openid4vp-ios-swift" */,
@@ -787,17 +787,17 @@
kind = branch;
};
};
9CE34B1D2BFE03E4001AF414 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */ = {
34873E452CD8DAF3004DE734 /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/mosip/pixelpass-ios-swift/";
repositoryURL = "https://github.com/mosip/inji-vci-client-ios-swift";
requirement = {
branch = develop;
kind = branch;
};
};
E86208242C039895007C3E24 /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */ = {
34873E4B2CD8DD11004DE734 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/mosip/inji-vci-client-ios-swift.git";
repositoryURL = "https://github.com/mosip/pixelpass-ios-swift";
requirement = {
branch = develop;
kind = branch;
@@ -821,14 +821,14 @@
package = 9C7CDF472C89802C00243A9A /* XCRemoteSwiftPackageReference "secure-keystore-ios-swift" */;
productName = securekeystore;
};
9CE34B1E2BFE03E4001AF414 /* pixelpass */ = {
34873E4C2CD8DD11004DE734 /* pixelpass */ = {
isa = XCSwiftPackageProductDependency;
package = 9CE34B1D2BFE03E4001AF414 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */;
package = 34873E4B2CD8DD11004DE734 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */;
productName = pixelpass;
};
E8AF2B9C2C0D93E800E775F6 /* VCIClient */ = {
34873E462CD8DAF3004DE734 /* VCIClient */ = {
isa = XCSwiftPackageProductDependency;
package = E86208242C039895007C3E24 /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */;
package = 34873E452CD8DAF3004DE734 /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */;
productName = VCIClient;
};
/* End XCSwiftPackageProductDependency section */

View File

@@ -1,5 +1,5 @@
{
"originHash" : "f9795fd8da608010a3959d5ff512f9c3efa9ac32efccde3511dfd65f1d22ce33",
"originHash" : "178f6c7c607eeb08b99a4966015d08339500de64791888a2e79d6b7afae53659",
"pins" : [
{
"identity" : "base45-swift",
@@ -40,19 +40,19 @@
{
"identity" : "inji-vci-client-ios-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/mosip/inji-vci-client-ios-swift.git",
"location" : "https://github.com/mosip/inji-vci-client-ios-swift",
"state" : {
"branch" : "develop",
"revision" : "0ff87e2eb1e6f078be8a004214cfaed8aae7fa93"
"revision" : "5fe56728106cffc8eff47e43437f288307c9d91b"
}
},
{
"identity" : "pixelpass-ios-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/mosip/pixelpass-ios-swift/",
"location" : "https://github.com/mosip/pixelpass-ios-swift",
"state" : {
"branch" : "develop",
"revision" : "170f3489e77940212f471ee1cb9f3ab392039a45"
"revision" : "7111692893287015599e4e0297bcade6a5375527"
}
},
{

View File

@@ -11,6 +11,9 @@ RCT_EXTERN_METHOD(decode:(NSString *)parameter
RCT_EXTERN_METHOD(generateQRData:(NSString *)data
header:(NSString *)header resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(decodeBase64UrlEncodedCBORData:(NSString *)data
resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
@end

View File

@@ -27,6 +27,16 @@ class RNPixelpassModule: NSObject, RCTBridgeModule {
reject("E_NO_IMAGE", "Unable to generate QR data", nil)
}
}
@objc
func decodeBase64UrlEncodedCBORData(_ data: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
do {
let decodedData = try PixelPass().toJson(base64UrlEncodedCborEncodedString: data)
resolve(decodedData)
} catch {
reject("ERROR_DECODING", "Unable to decode data", nil)
}
}
@objc
static func requiresMainQueueSetup() -> Bool {

View File

@@ -7,7 +7,6 @@ import {
MY_VCS_STORE_KEY,
NETWORK_REQUEST_FAILED,
REQUEST_TIMEOUT,
TECHNICAL_ERROR,
isIOS,
} from '../../shared/constants';
import {assign, send} from 'xstate';
@@ -168,10 +167,21 @@ export const IssuersActions = (model: any) => {
storeVerifiableCredentialData: send(
(context: any) => {
const vcMeatadata = getVCMetadata(context, context.keyType);
return StoreEvents.SET(vcMeatadata.getVcKey(), {
...context.credentialWrapper,
vcMetadata: vcMeatadata,
const vcMetadata = getVCMetadata(context, context.keyType);
const {
verifiableCredential: {
processedCredential,
...filteredVerifiableCredential
},
...rest
} = context.credentialWrapper;
const storableData = {
...rest,
verifiableCredential: filteredVerifiableCredential,
};
return StoreEvents.SET(vcMetadata.getVcKey(), {
...storableData,
vcMetadata: vcMetadata,
});
},
{

View File

@@ -15,13 +15,18 @@ import {
generateKeyPair,
} from '../../shared/cryptoutil/cryptoUtil';
import {NativeModules} from 'react-native';
import {verifyCredential} from '../../shared/vcjs/verifyCredential';
import {
VerificationErrorMessage,
VerificationErrorType,
verifyCredential,
} from '../../shared/vcjs/verifyCredential';
import {
getImpressionEventData,
sendImpressionEvent,
} from '../../shared/telemetry/TelemetryUtils';
import {TelemetryConstants} from '../../shared/telemetry/TelemetryConstants';
import {VciClient} from '../../shared/vciClient/VciClient';
import {isMockVC} from '../../shared/Utils';
export const IssuersService = () => {
return {
@@ -75,7 +80,7 @@ export const IssuersService = () => {
);
console.info(`VC download via ${context.selectedIssuerId} is successful`);
return updateCredentialInformation(context, credential);
return await updateCredentialInformation(context, credential);
},
invokeAuthorization: async (context: any) => {
sendImpressionEvent(
@@ -119,12 +124,21 @@ export const IssuersService = () => {
},
verifyCredential: async (context: any) => {
const verificationResult = await verifyCredential(
context.verifiableCredential?.credential,
context.selectedCredentialType.format,
);
if (!verificationResult.isVerified) {
throw new Error(verificationResult.verificationErrorCode);
//TODO: Remove bypassing verification of mock VCs once mock VCs are verifiable
if (!isMockVC(context.selectedIssuerId)) {
const verificationResult = await verifyCredential(
context.verifiableCredential?.credential,
context.selectedCredentialType.format,
);
if (!verificationResult.isVerified) {
throw new Error(verificationResult.verificationErrorCode);
}
} else {
return {
isVerified: true,
verificationMessage: VerificationErrorMessage.NO_ERROR,
verificationErrorCode: VerificationErrorType.NO_ERROR,
};
}
},
};

View File

@@ -7,6 +7,8 @@ import {
VerifiableCredential,
VerifiableCredentialData,
} from '../VCMetaMachine/vc';
import {VCFormat} from '../../../shared/VCFormat';
import {VCProcessor} from '../../../components/VC/common/VCProcessor';
type State = StateFrom<typeof VCItemMachine>;
@@ -36,8 +38,8 @@ export function getVerifiableCredential(
return verifiableCredential?.credential || verifiableCredential;
}
export function selectCredential(state: State): Credential {
return getVerifiableCredential(state.context.verifiableCredential);
export function selectCredential(state: State): VerifiableCredential {
return state.context.verifiableCredential;
}
export function selectVerifiableCredentialData(

View File

@@ -43,7 +43,7 @@ export interface CredentialSubject {
type VCContext = (string | Record<string, unknown>)[];
export interface Credential {
export type Credential = {
credentialConfigurationId: any;
'@context': VCContext;
credentialSubject: CredentialSubject;
@@ -58,11 +58,12 @@ export interface Credential {
verificationMethod: string;
};
type: string[];
}
} | string
export interface VerifiableCredential {
issuerLogo: logoType;
credential: Credential;
processedCredential?: object;
wellKnown: string;
credentialConfigurationId: string;
}

View File

@@ -2,7 +2,7 @@ import {StateFrom} from 'xstate';
import {requestMachine} from './requestMachine';
import {VCMetadata} from '../../../shared/VCMetadata';
import {getMosipLogo} from '../../../components/VC/common/VCUtils';
import {Credential} from '../../VerifiableCredential/VCMetaMachine/vc';
import {VerifiableCredential} from '../../VerifiableCredential/VCMetaMachine/vc';
type State = StateFrom<typeof requestMachine>;
@@ -10,11 +10,8 @@ export function selectSenderInfo(state: State) {
return state.context.senderInfo;
}
export function selectCredential(state: State): Credential {
return (
state.context.incomingVc?.verifiableCredential?.credential ||
state.context.incomingVc?.verifiableCredential
);
export function selectCredential(state: State): VerifiableCredential {
return state.context.incomingVc?.verifiableCredential;
}
export function selectVerifiableCredentialData(state: State) {

View File

@@ -33,12 +33,29 @@ import {
BannerNotification,
BannerStatus,
} from '../../components/BannerNotification';
import {VCProcessor} from '../../components/VC/common/VCProcessor';
export const ViewVcModal: React.FC<ViewVcModalProps> = props => {
const {t} = useTranslation('ViewVcModal');
const controller = useViewVcModal(props);
const profileImage = controller.verifiableCredentialData.face;
const verificationStatus = controller.verificationStatus;
const [verifiableCredential, setVerifiableCredential] = useState(null);
useEffect(() => {
async function processVC() {
if (controller.credential) {
const vcData = await VCProcessor.processForRendering(
controller.credential,
controller.verifiableCredentialData.format,
);
setVerifiableCredential(vcData);
}
}
processVC();
}, [controller.credential]);
useEffect(() => {
if (controller.isVerificationInProgress) {
controller.SHOW_VERIFICATION_STATUS_BANNER();
@@ -85,7 +102,7 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = props => {
</LinearGradient>
}
/>
{isVCLoaded(controller.credential, fields) ? (
{isVCLoaded(verifiableCredential, fields) ? (
<Pressable
onPress={() => props.vcItemActor.send('KEBAB_POPUP')}
accessible={false}>
@@ -143,13 +160,14 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = props => {
/>
)}
{!isVCLoaded(controller.credential, fields) ? (
{!isVCLoaded(verifiableCredential, fields) ? (
<ActivityIndicator />
) : (
<VcDetailsContainer
fields={fields}
wellknown={wellknown}
credential={controller.credential}
credential={verifiableCredential}
credentialWrapper={controller.credential}
verifiableCredentialData={controller.verifiableCredentialData}
onBinding={controller.addtoWallet}
walletBindingResponse={controller.walletBindingResponse}

View File

@@ -12,7 +12,6 @@ import {
selectOtpError,
selectShowWalletBindingError,
selectVc,
selectCredential,
selectVerifiableCredentialData,
selectWalletBindingError,
selectWalletBindingInProgress,
@@ -22,6 +21,7 @@ import {
selectIsVerificationInProgress,
selectShowVerificationStatusBanner,
selectIsVerificationCompleted,
selectCredential,
} from '../../machines/VerifiableCredential/VCItemMachine/VCItemSelectors';
import {selectPasscode} from '../../machines/auth';
import {biometricsMachine, selectIsSuccess} from '../../machines/biometrics';

View File

@@ -11,6 +11,7 @@ import {SharingStatusModal} from '../Scan/SharingStatusModal';
import {SvgImage} from '../../components/ui/svg';
import {DETAIL_VIEW_DEFAULT_FIELDS} from '../../components/VC/common/VCUtils';
import {getDetailedViewFields} from '../../shared/openId4VCI/Utils';
import { VCProcessor } from '../../components/VC/common/VCProcessor';
export const ReceiveVcScreen: React.FC = () => {
const {t} = useTranslation('ReceiveVcScreen');
@@ -23,6 +24,22 @@ export const ReceiveVcScreen: React.FC = () => {
const verifiableCredentialData = controller.verifiableCredentialData;
const profileImage = verifiableCredentialData.face;
const [credential, setCredential] = useState(null);
useEffect(() => {
async function processVC() {
if (controller.credential) {
const vcData = await VCProcessor.processForRendering(
controller.credential,
verifiableCredentialData.vcMetadata.format,
);
setCredential(vcData);
}
}
processVC();
}, [controller.credential]);
useEffect(() => {
getDetailedViewFields(
verifiableCredentialData?.issuer,
@@ -54,7 +71,8 @@ export const ReceiveVcScreen: React.FC = () => {
<VcDetailsContainer
fields={fields}
wellknown={wellknown}
credential={controller.credential}
credential={credential}
credentialWrapper={controller.credential}
verifiableCredentialData={verifiableCredentialData}
isBindingPending={false}
activeTab={1}

View File

@@ -11,7 +11,7 @@ import {
getImpressionEventData,
sendImpressionEvent,
} from '../../shared/telemetry/TelemetryUtils';
import {isMosipVC, VCItemContainerFlowType} from '../../shared/Utils';
import {VCItemContainerFlowType} from '../../shared/Utils';
import {VCMetadata} from '../../shared/VCMetadata';
import {VerifyIdentityOverlay} from '../VerifyIdentityOverlay';
import {VPShareOverlay} from './VPShareOverlay';

View File

@@ -80,8 +80,19 @@ export function useSendVcScreen() {
SELECT_VC_ITEM:
(index: number) => (vcRef: ActorRefFrom<typeof VCItemMachine>) => {
setSelectedIndex(index);
const {serviceRefs, wellknownResponse, ...vcData} =
vcRef.getSnapshot().context;
const {
serviceRefs,
wellknownResponse,
verifiableCredential: {
processedCredential,
...filteredVerifiableCredential
},
...rest
} = vcRef.getSnapshot().context;
const vcData = {
...rest,
verifiableCredential: filteredVerifiableCredential,
};
scanService.send(
ScanEvents.SELECT_VC(vcData, VCShareFlowType.SIMPLE_SHARE),
);

View File

@@ -35,6 +35,10 @@ export const isMosipVC = (issuer: string) => {
return issuer === Issuers.Mosip || issuer === Issuers.MosipOtp;
};
export const isMockVC = (issuer: string) => {
return issuer.toLowerCase().startsWith('mock');
};
export const parseJSON = (input: any) => {
let result = null;
try {

View File

@@ -4,7 +4,7 @@ import {
VcIdType,
VerifiableCredential,
} from '../machines/VerifiableCredential/VCMetaMachine/vc';
import {CredentialIdForMsoMdoc, Protocols} from './openId4VCI/Utils';
import {Protocols} from './openId4VCI/Utils';
import {getMosipIdentifier} from './commonUtil';
import {VCFormat} from './VCFormat';
@@ -67,7 +67,7 @@ export class VCMetadata {
? vc.displayId
: vc.vcMetadata
? vc.vcMetadata.displayId
: getDisplayId(vc.verifiableCredential),
: getDisplayId(vc.verifiableCredential, vc.format),
downloadKeyType: vc.downloadKeyType,
});
}
@@ -119,27 +119,53 @@ export const getVCMetadata = (context: object, keyType: string) => {
id: `${credentialId} + '_' + ${issuer}`,
timestamp: context.timestamp ?? '',
isVerified: context.vcMetadata.isVerified ?? false,
displayId: getDisplayId(context.verifiableCredential),
format: context.credentialWrapper.format,
displayId: getDisplayId(
context['verifiableCredential'] as VerifiableCredential,
context['credentialWrapper'].format,
),
format: context['credentialWrapper'].format,
downloadKeyType: keyType,
});
};
const getDisplayId = (
verifiableCredential: VerifiableCredential | Credential,
format: string,
) => {
if (verifiableCredential?.credential) {
if (verifiableCredential.credential?.credentialSubject) {
return (
verifiableCredential.credential?.credentialSubject?.policyNumber ||
getMosipIdentifier(verifiableCredential.credential.credentialSubject)
);
} else {
return CredentialIdForMsoMdoc(verifiableCredential);
try {
if (format === VCFormat.mso_mdoc) {
const namespaces =
(verifiableCredential as VerifiableCredential)?.processedCredential?.[
'issuerSigned'
]['nameSpaces'] ?? {};
let displayId: string | undefined;
for (const namespace in namespaces) {
displayId = namespaces[namespace].find(
(element: object) =>
element['elementIdentifier'] === 'document_number',
).elementValue;
if (!!displayId) break;
}
if (!!displayId) return displayId;
console.error('error in id getting ', 'Id not found for the credential');
throw new Error('Id not found for the credential');
}
if (verifiableCredential?.credential) {
if (verifiableCredential.credential?.credentialSubject) {
return (
verifiableCredential.credential?.credentialSubject?.policyNumber ||
getMosipIdentifier(verifiableCredential.credential.credentialSubject)
);
}
}
return (
verifiableCredential?.credentialSubject?.policyNumber ||
getMosipIdentifier(verifiableCredential.credentialSubject)
);
} catch (error) {
console.error('Error getting the display Id - ', error);
return null;
}
return (
verifiableCredential?.credentialSubject?.policyNumber ||
getMosipIdentifier(verifiableCredential.credentialSubject)
);
};

View File

@@ -26,6 +26,7 @@ import {KeyTypes} from '../cryptoutil/KeyTypes';
import {VCFormat} from '../VCFormat';
import {UnsupportedVcFormat} from '../error/UnsupportedVCFormat';
import {VCMetadata} from '../VCMetadata';
import {VCProcessor} from '../../components/VC/common/VCProcessor';
export const Protocols = {
OpenId4VCI: 'OpenId4VCI',
@@ -62,49 +63,64 @@ export const isActivationNeeded = (issuer: string) => {
export const Issuers_Key_Ref = 'OpenId4VCI_KeyPair';
export const getIdentifier = (context, credential: VerifiableCredential) => {
const credentialIdentifier = credential.credential.id;
if (credentialIdentifier === undefined) {
return (
context.selectedIssuer.credential_issuer +
':' +
context.selectedIssuer.protocol +
':' +
CredentialIdForMsoMdoc(credential)
);
} else {
const credId = credentialIdentifier.startsWith('did')
? credentialIdentifier.split(':')
: credentialIdentifier.split('/');
return (
context.selectedIssuer.credential_issuer +
':' +
context.selectedIssuer.protocol +
':' +
credId[credId.length - 1]
);
}
};
export const updateCredentialInformation = (
export const getIdentifier = (
context,
credential: VerifiableCredential,
): CredentialWrapper => {
format: string,
) => {
let credentialIdentifier = '';
if (format === VCFormat.mso_mdoc) {
credentialIdentifier = credential?.processedCredential?.['id'] ?? '';
} else if (typeof credential.credential !== 'string') {
credentialIdentifier = credential.credential.id;
}
const credId =
credentialIdentifier.startsWith('did') ||
credentialIdentifier.startsWith('urn:')
? credentialIdentifier.split(':')
: credentialIdentifier.split('/');
return (
context.selectedIssuer.credential_issuer +
':' +
context.selectedIssuer.protocol +
':' +
credId[credId.length - 1]
);
};
export const updateCredentialInformation = async (
context,
credential: VerifiableCredential,
): Promise<CredentialWrapper> => {
let processedCredential;
if (context.selectedCredentialType.format === VCFormat.mso_mdoc) {
processedCredential = await VCProcessor.processForRendering(
credential,
context.selectedCredentialType.format,
);
}
const verifiableCredential = {
...credential,
wellKnown: context.selectedIssuer['wellknown_endpoint'],
credentialConfigurationId: context.selectedCredentialType.id,
issuerLogo: getDisplayObjectForCurrentLanguage(
context.selectedIssuer.display,
)?.logo,
processedCredential,
};
return {
verifiableCredential: {
...credential,
wellKnown: context.selectedIssuer['wellknown_endpoint'],
credentialConfigurationId: context.selectedCredentialType.id,
issuerLogo: getDisplayObjectForCurrentLanguage(
context.selectedIssuer.display,
)?.logo,
},
verifiableCredential,
format: context.selectedCredentialType.format,
identifier: getIdentifier(context, credential),
identifier: getIdentifier(
context,
verifiableCredential,
context.selectedCredentialType.format,
),
generatedOn: new Date(),
vcMetadata:
{...context.vcMetadata, format: context.selectedCredentialType.format} ||
{},
vcMetadata: {
...context.vcMetadata,
format: context.selectedCredentialType.format,
},
};
};
@@ -259,13 +275,6 @@ export enum ErrorMessage {
CREDENTIAL_TYPE_DOWNLOAD_FAILURE = 'credentialTypeListDownloadFailure',
}
export function CredentialIdForMsoMdoc(credential: VerifiableCredential) {
return credential.credential['issuerSigned']['nameSpaces'][
'org.iso.18013.5.1'
].find(element => element.elementIdentifier === 'document_number')
.elementValue;
}
export async function constructProofJWT(
publicKey: any,
privateKey: any,

View File

@@ -34,7 +34,6 @@ import fileStorage from './fileStorage';
import {DocumentDirectoryPath, ReadDirItem} from 'react-native-fs';
import {verifyCredential} from './vcjs/verifyCredential';
import {Credential} from '../machines/VerifiableCredential/VCMetaMachine/vc';
import {isMosipVC} from './Utils';
export const MMKV = new MMKVLoader().initialize();
const {RNSecureKeystoreModule} = NativeModules;