Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attestations v4 #168

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,21 @@ android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
package.json
yarn.lock
android/app/src/main/assets/index.android.bundle
android/app/src/main/res/drawable-hdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png
android/app/src/main/res/drawable-hdpi/node_modules_reactnavigation_elements_src_assets_backicon.png
android/app/src/main/res/drawable-mdpi/node_modules_reactnative_libraries_newappscreen_components_logo.png
android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png
android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_src_assets_backicon.png
android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_src_assets_backiconmask.png
android/app/src/main/res/drawable-mdpi/src_images_customicons_coinsicon.png
android/app/src/main/res/drawable-mdpi/src_images_customicons_recovericon.png
android/app/src/main/res/drawable-mdpi/src_images_customicons_revokeicon.png
android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png
android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_src_assets_backicon.png
android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png
android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_src_assets_backicon.png
android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_drawer_src_views_assets_toggledrawericon.png
android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_src_assets_backicon.png
1 change: 1 addition & 0 deletions env/main.android.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"SETTINGS_STORAGE_INTERNAL_KEY": "settings",
"BLOCK_HEIGHT_STORAGE_INTERNAL_KEY": "blockHeight",
"PERSONAL_DATA_STORAGE_INTERNAL_KEY": "personal",
"ATTESTATION_DATA_STORAGE_INTERNAL_KEY": "attestation",
"SERVICE_STORAGE_INTERNAL_KEY": "serviceStoredData",
"NOTIFICATIONS_STORAGE_INTERNAL_KEY": "notifications",
"WIDGET_STORAGE_INTERNAL_KEY": "widgets",
Expand Down
1 change: 1 addition & 0 deletions env/main.ios.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"SETTINGS_STORAGE_INTERNAL_KEY": "settings",
"BLOCK_HEIGHT_STORAGE_INTERNAL_KEY": "blockHeight",
"PERSONAL_DATA_STORAGE_INTERNAL_KEY": "personal",
"ATTESTATION_DATA_STORAGE_INTERNAL_KEY": "attestation",
"SERVICE_STORAGE_INTERNAL_KEY": "serviceStoredData",
"NOTIFICATIONS_STORAGE_INTERNAL_KEY": "notifications",
"WIDGET_STORAGE_INTERNAL_KEY": "widgets",
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ const IGNORED_LOGS = [

LogBox.ignoreLogs(IGNORED_LOGS);

import './shims/crypto.js';
import App from './App';
import {name as appName} from './app.json';
import './shims/crypto.js';
import './shims/stringify.js';

AppRegistry.registerComponent(appName, () => App);
1 change: 1 addition & 0 deletions ios/assets/env/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"SETTINGS_STORAGE_INTERNAL_KEY": "settings",
"BLOCK_HEIGHT_STORAGE_INTERNAL_KEY": "blockHeight",
"PERSONAL_DATA_STORAGE_INTERNAL_KEY": "personal",
"ATTESTATION_DATA_STORAGE_INTERNAL_KEY": "attestation",
"SERVICE_STORAGE_INTERNAL_KEY": "serviceStoredData",
"NOTIFICATIONS_STORAGE_INTERNAL_KEY": "notifications",
"WIDGET_STORAGE_INTERNAL_KEY": "widgets",
Expand Down
4 changes: 4 additions & 0 deletions metro.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ module.exports = (async () => {
resolver: {
assetExts: assetExts.filter(ext => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
extraNodeModules: {
stream: require.resolve('readable-stream'),
crypto: require.resolve('react-native-crypto'),
}
},
};
})();
4 changes: 3 additions & 1 deletion package.json
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
"uuid": "8.0.0",
"version_compare": "0.0.3",
"verus-pay-utils": "git+https://github.com/michaeltout/verus-pay-utils.git",
"verus-typescript-primitives": "git+https://github.com/VerusCoin/verus-typescript-primitives.git",
"verus-typescript-primitives": "file:d:/dev/verus-typescript-primitives",
"verus-zkedid-utils": "git+https://github.com/michaeltout/verus-zkedid-utils.git",
"verusd-rpc-ts-client": "git+https://github.com/VerusCoin/verusd-rpc-ts-client.git",
"verusid-ts-client": "git+https://github.com/VerusCoin/verusid-ts-client.git",
Expand Down Expand Up @@ -220,8 +220,10 @@
"@sideway/formula": "3.0.1",
"@babel/traverse": "7.23.2",
"@babel/core": "7.21.3",
"@bitgo/utxo-lib": "git+https://github.com/VerusCoin/BitGoJS.git#7c754d4a5920198d9fe6827d3e23bd5cf431f264",
"blake2b-wasm": "https://github.com/michaeltout/blake2b-wasm/",
"@bitgo/blake2b-wasm": "https://github.com/michaeltout/blake2b-wasm/",
"verus-typescript-primitives": "file:d:/dev/verus-typescript-primitives",
"browserify-sign": "4.2.2"
},
"jest": {
Expand Down
26 changes: 26 additions & 0 deletions shim.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
if (typeof __dirname === 'undefined') global.__dirname = '/'
if (typeof __filename === 'undefined') global.__filename = ''
if (typeof process === 'undefined') {
global.process = require('process')
} else {
const bProcess = require('process')
for (var p in bProcess) {
if (!(p in process)) {
process[p] = bProcess[p]
}
}
}

process.browser = false
if (typeof Buffer === 'undefined') global.Buffer = require('buffer').Buffer

// global.location = global.location || { port: 80 }
const isDev = typeof __DEV__ === 'boolean' && __DEV__
process.env['NODE_ENV'] = isDev ? 'development' : 'production'
if (typeof localStorage !== 'undefined') {
localStorage.debug = isDev ? '*' : ''
}

// If using the crypto shim, uncomment the following line to ensure
// crypto is loaded first, so it can populate global.crypto
// require('crypto')
2 changes: 2 additions & 0 deletions src/actions/actions/account/dispatchers/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
clearChainLifecycle,
} from "../../intervals/dispatchers/lifecycleManager";
import { initPersonalDataForUser } from "../../personal/dispatchers/personal";
import { initAttestationDataForUser } from "../../attestations/dispatchers/attestations";
import { initServiceStoredDataForUser } from "../../services/dispatchers/services";
import { fetchUsers, validateLogin } from "../../UserData";
import { initSettings, saveGeneralSettings } from "../../WalletSettings";
Expand Down Expand Up @@ -100,6 +101,7 @@ export const initializeAccountData = async (

activateServiceLifecycle();
await initPersonalDataForUser(account.accountHash);
await initAttestationDataForUser(account.accountHash);
store.dispatch(signIntoAuthenticatedAccount());
} else {
throw new Error(
Expand Down
10 changes: 10 additions & 0 deletions src/actions/actions/attestations/creators/attestations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { SET_ATTESTATION_DATA } from "../../../../utils/constants/storeType"

export const setAttestationData = (
data = {
attestations_provisioned: null
}
) => ({
type: SET_ATTESTATION_DATA,
data,
});
31 changes: 31 additions & 0 deletions src/actions/actions/attestations/dispatchers/attestations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import store from "../../../../store"
import { deleteAttestationDataForUser, loadAttestationDataForUser, storeAttestationDataForUser } from "../../../../utils/nativeStore/attestationDataStorage"
import { requestPassword } from "../../../../utils/auth/authBox"
import { encryptkey } from "../../../../utils/seedCrypt"
import { setAttestationData } from "../creators/attestations"

export const saveEncryptedAttestationDataForUser = async (encryptedData = {}, accountHash) => {
const attestationData = await storeAttestationDataForUser(encryptedData, accountHash)
store.dispatch(setAttestationData(encryptedData))
return attestationData
}

export const clearEncryptedAttestationDataForUser = async (accountHash) => {
const attestationData = await deleteAttestationDataForUser(accountHash)
store.dispatch(setAttestationData({}))
return attestationData
}

export const modifyAttestationDataForUser = async (data = {}, dataType, accountHash) => {
let attestationData = {...(await loadAttestationDataForUser(accountHash))}
attestationData[dataType] = await encryptkey(await requestPassword(), JSON.stringify(data))
await saveEncryptedAttestationDataForUser(attestationData, accountHash)

return data
}

export const initAttestationDataForUser = async (accountHash) => {
const attestationData = await loadAttestationDataForUser(accountHash)
store.dispatch(setAttestationData(attestationData))
return attestationData
}
7 changes: 6 additions & 1 deletion src/containers/DeepLink/DeepLink.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ import { getCurrency, getIdentity } from '../../utils/api/channels/verusid/callC
import { convertFqnToDisplayFormat } from '../../utils/fullyqualifiedname';
import { resetDeeplinkData } from '../../actions/actionCreators';

const authorizedPermissions = [primitives.IDENTITY_VIEW.vdxfid,/*TODO: primitives.IDENTITY_AGREEMENT.vdxfid, primitives.ATTESTATION_READ_REQUEST.vdxfid */]
const authorizedPermissions = [primitives.IDENTITY_VIEW.vdxfid,
primitives.IDENTITY_AGREEMENT.vdxfid,
primitives.ATTESTATION_READ_REQUEST.vdxfid,
primitives.PROFILE_DATA_VIEW_REQUEST.vdxfid,
primitives.LOGIN_CONSENT_PERSONALINFO_WEBHOOK_VDXF_KEY.vdxfid,
]
import { CoinDirectory } from '../../utils/CoinData/CoinDirectory';
import BigNumber from 'bignumber.js';
import { blocksToTime, satsToCoins } from '../../utils/math';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { Component } from "react"
import { connect } from 'react-redux'
import { createAlert, resolveAlert } from "../../../actions/actions/alert/dispatchers/alert"
import { LoginReceiveAttestationRender } from "./LoginReceiveAttestation.render"
import { primitives } from "verusid-ts-client"
import { VDXFDataToUniValueArray } from "verus-typescript-primitives/dist/vdxf/classes/DataDescriptor.js";
import * as VDXF_Data from "verus-typescript-primitives/dist/vdxf/vdxfDataKeys";
import { SignatureData } from "verus-typescript-primitives/dist/vdxf/classes/SignatureData";
import { verifyHash } from "../../../utils/api/channels/vrpc/requests/verifyHash";
import { getSignatureInfo } from "../../../utils/api/channels/vrpc/requests/getSignatureInfo";
const { ATTESTATION_NAME } = primitives;
import { IdentityVdxfidMap } from "verus-typescript-primitives/dist/vdxf/classes/IdentityData";
import { ATTESTATIONS_PROVISIONED } from "../../../utils/constants/attestations";
import { modifyAttestationDataForUser } from "../../../actions/actions/attestations/dispatchers/attestations";

class LoginReceiveAttestation extends Component {
constructor(props) {
super(props);
this.state = {
loginConsent: null,
loading: false,
ready: false,
personalDataURL: "",
signerFqn: this.props.route.params.signerFqn,
attestationName: "",
attestationData: {},
completeAttestaton: {},
};
const dfg = 2111111
}

componentDidMount() {
this.updateDisplay();
}

cancel = () => {
if (this.props.route.params.cancel) {
this.props.route.params.cancel.cancel()
}
}

validateAttestation = async (signatureData) => {

const sigObject = SignatureData.fromJson(signatureData);

const sigInfo = await getSignatureInfo(
sigObject.systemID,
sigObject.identityID,
sigObject.signatureAsVch.toString('base64'),
);

return await verifyHash(sigObject.systemID, sigObject.identityID, sigObject.signatureAsVch.toString('base64'), sigObject.getIdentityHash(sigInfo));

}

getAttestationData = (dataDescriptors) => {

const data = {};
dataDescriptors.forEach((dataDescriptor) => {
const label = dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].label;
let key = "";

if (label === ATTESTATION_NAME.vdxfid) {
key = `Attestation name`
} else {
key = IdentityVdxfidMap[label]?.name || label;
}

const mime = dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].mimetype || "";
if (mime.startsWith("text/")) {
data[key] = { "message": dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].objectdata.message };
} else if (mime.startsWith("image/")) {
if (mime === "image/jpeg" || mime === "image/png") {
data[key] = { "image": `data:${mime};base64,${Buffer.from(dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].objectdata, "hex").toString("base64")}` };
}
}
});

return data;

}

updateDisplay() {
const { deeplinkData } = this.props.route.params
const loginConsent = new primitives.LoginConsentRequest(deeplinkData);

if (loginConsent.challenge.attestations && loginConsent.challenge.attestations.length > 1) {
createAlert("Error", "Only one attestation is allowed to be received at a time.");
this.cancel();
return;
}

const checkAttestation = loginConsent.challenge.attestations[0];

if (checkAttestation.vdxfkey === primitives.ATTESTATION_PROVISION_OBJECT.vdxfid) {

const dataDescriptorObject = VDXFDataToUniValueArray(Buffer.from(checkAttestation.data, "hex"));

if (!Array.isArray(dataDescriptorObject)) {
createAlert("Error", "Invalid data descriptor object in Attestation.");
this.cancel();
return;
}

if (dataDescriptorObject.some((dataDescriptor) => Object.keys(dataDescriptor)[0] === VDXF_Data.DataURLKey().vdxfid)) {

// TODO: Handle fetch data from URL
}
else if (dataDescriptorObject.some((dataDescriptor) => Object.keys(dataDescriptor)[0] === VDXF_Data.MMRDescriptorKey().vdxfid) &&
dataDescriptorObject.some((dataDescriptor) => Object.keys(dataDescriptor)[0] === VDXF_Data.SignatureDataKey().vdxfid)) {

const signatureData = dataDescriptorObject.find((dataDescriptor) => Object.keys(dataDescriptor)[0] === VDXF_Data.SignatureDataKey().vdxfid)[VDXF_Data.SignatureDataKey().vdxfid];
const mmrData = dataDescriptorObject.find((dataDescriptor) => Object.keys(dataDescriptor)[0] === VDXF_Data.MMRDescriptorKey().vdxfid)[VDXF_Data.MMRDescriptorKey().vdxfid];

if (!this.validateAttestation(signatureData) || mmrData.mmrroot.objectdata != signatureData.signaturehash) {
createAlert("Error", "Invalid attestation signature.");
this.cancel();
return;
}

const attestationName = mmrData.datadescriptors.find((dataDescriptor) => dataDescriptor.objectdata[Object.keys(dataDescriptor.objectdata)[0]].label === ATTESTATION_NAME.vdxfid)?.objectdata;

if (!attestationName || Object.values(attestationName)[0].label !== ATTESTATION_NAME.vdxfid) {
createAlert("Error", "Attestation has no name.");
this.cancel();
return;
} else {

this.setState({ attestationName: Object.values(attestationName)[0].objectdata.message });

}

const containingData = this.getAttestationData(mmrData.datadescriptors);
this.setState({ attestationData: containingData,
completeAttestaton: {[loginConsent.getChallengeHash(1).toString('base64')]:{ name: Object.values(attestationName)[0].objectdata.message,
signer: this.state.signerFqn,
data: dataDescriptorObject}}
});

} else {
createAlert("Error", "Invalid attestation type.");
this.cancel();
return;
}

}
}

handleContinue() {
this.setState(
{ loading: true },
async () => {
await modifyAttestationDataForUser(
this.state.completeAttestaton,
ATTESTATIONS_PROVISIONED,
this.props.activeAccount.accountHash
);

this.setState({ loading: false });
this.props.route.params.onGoBack(true);
this.props.navigation.goBack();
this.cancel();
}
);
};


render() {
return LoginReceiveAttestationRender.call(this);
}
}

const mapStateToProps = (state) => {
return {
activeAccount: state.authentication.activeAccount,
encryptedPersonalData: state.personal
}
};

export default connect(mapStateToProps)(LoginReceiveAttestation);
Loading