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

fix: issues #232

Merged
merged 4 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions apps/easypid/src/app/(app)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,20 @@ export default function AppLayout() {
// It could be that the onboarding is cut of mid-process, and e.g. the user closes the app
// if this is the case we will redo the onboarding
const [hasFinishedOnboarding] = useHasFinishedOnboarding()
const [resetWalletState, setResetWalletState] = useState<'resetting' | 'reset'>()
const [hasResetWallet, setHasResetWallet] = useState(false)
const shouldResetWallet =
secureUnlock.state !== 'not-configured' && secureUnlock.state !== 'initializing' && !hasFinishedOnboarding
const isWalletLocked = secureUnlock.state === 'locked' || secureUnlock.state === 'acquired-wallet-key'

useEffect(() => {
if (resetWalletState || !shouldResetWallet) return
// Reset state
if (hasResetWallet && !shouldResetWallet) setHasResetWallet(false)
if (!shouldResetWallet || hasResetWallet) return

setResetWalletState('resetting')
resetWallet(secureUnlock).then(() => setResetWalletState('reset'))
}, [secureUnlock, resetWalletState, shouldResetWallet])
console.log('Resetting wallet')
setHasResetWallet(true)
resetWallet(secureUnlock)
}, [secureUnlock, hasResetWallet, shouldResetWallet])

// If we are intializing and the wallet was opened using a deeplinkg we will be redirected
// to the authentication screen. We first save the redirection url and use that when navigation
Expand All @@ -66,7 +69,7 @@ export default function AppLayout() {
}

// This should show the splash screen
if (secureUnlock.state === 'initializing' || (shouldResetWallet && resetWalletState !== 'reset')) {
if (secureUnlock.state === 'initializing' || shouldResetWallet) {
return null
}

Expand Down
2 changes: 1 addition & 1 deletion apps/easypid/src/app/+native-intent.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { parseInvitationUrl } from '@package/agent'
import { deeplinkSchemes } from '@package/app'
import * as Haptics from 'expo-haptics'
import { Platform } from 'react-native'
import { router } from 'expo-router'
import { Platform } from 'react-native'

export async function redirectSystemPath({ path, initial }: { path: string; initial: boolean }) {
const isRecognizedDeeplink = deeplinkSchemes.some((scheme) => path.startsWith(scheme))
Expand Down
6 changes: 5 additions & 1 deletion apps/easypid/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ const animoFunkeRelyingPartyCertificate =
const ubiqueRootCertificate =
'MIIBZjCCAQygAwIBAgIGAZGJt173MAoGCCqGSM49BAMCMB8xHTAbBgNVBAMMFGh0dHBzOi8vYXV0aG9yaXR5LmNoMB4XDTI0MDgyNTEzMjYyMVoXDTI1MDgyNTEzMjYyMVowHzEdMBsGA1UEAwwUaHR0cHM6Ly9hdXRob3JpdHkuY2gwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAScIjAmHrkp3TC6bisgaqmszbKkpY0iGTdHF2rcRemJCV+ikotDt7G+ApwG0m6fxt8aBJHeJ2mssLvZBmZj5LtWozQwMjAfBgNVHREEGDAWghRodHRwczovL2F1dGhvcml0eS5jaDAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIQCpQsxyQx/5knqhGnDCiAo6MpQmTCd7vA9WehF4/1P8/QIgEnAtFVTP1uThuTEna1RD4Ji35+z1h8pDoMyLPd3Uaig='

const ubiqueIssuer =
'MIICsTCCAlegAwIBAgIUeN7cTJgPmbK39asN8Wf3VLOSCTAwCgYIKoZIzj0EAwIwgcYxCzAJBgNVBAYTAkRFMR0wGwYDVQQIDBRHZW1laW5kZSBNdXN0ZXJzdGFkdDEUMBIGA1UEBwwLTXVzdGVyc3RhZHQxHTAbBgNVBAoMFEdlbWVpbmRlIE11c3RlcnN0YWR0MQswCQYDVQQLDAJJVDEpMCcGA1UEAwwgaXNzdWFuY2UuZ2VtZWluZGUtbXVzdGVyc3RhZHQuZGUxKzApBgkqhkiG9w0BCQEWHHRlc3RAZ2VtZWluZGUtbXVzdGVyc3RhZHQuZGUwHhcNMjQxMTE1MDgzNzA4WhcNMzQxMTEzMDgzNzA4WjCBxjELMAkGA1UEBhMCREUxHTAbBgNVBAgMFEdlbWVpbmRlIE11c3RlcnN0YWR0MRQwEgYDVQQHDAtNdXN0ZXJzdGFkdDEdMBsGA1UECgwUR2VtZWluZGUgTXVzdGVyc3RhZHQxCzAJBgNVBAsMAklUMSkwJwYDVQQDDCBpc3N1YW5jZS5nZW1laW5kZS1tdXN0ZXJzdGFkdC5kZTErMCkGCSqGSIb3DQEJARYcdGVzdEBnZW1laW5kZS1tdXN0ZXJzdGFkdC5kZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDYXt8M+5E1ADj5N2Rv/zIwBlvkTlt3gsscrKP4owg6km9Ejv5bHqDWY+nQi29ezNH2tkhGrKe0ZsmeH9ZqUsI+jITAfMB0GA1UdDgQWBBRSW2AGYj1dJ5Nz84/XojDDjH00XzAKBggqhkjOPQQDAgNIADBFAiBJ7ohG3x9iBlbTeSLnJGTFdwfw10mM9sd1J/TpoijcfAIhALgJgE/w3/J7jJMvZq+EiUT8DkhKTTUNhN74uA+bL4v6'

export const trustedX509Certificates = [
ubiqueIssuer,
bdrPidIssuerCertificate,
animoFunkeRelyingPartyCertificate,
ubiqueRootCertificate,
oldAnimoFunkeRelyingPartyCertificate,
'MIIBKDCBzqADAgECAhAyWHL4SEss2wMO1QQybg/fMAoGCCqGSM49BAMCMA0xCzAJBgNVBAYTAk5MMB4XDTcwMDEwMTAwMDAwMFoXDTI1MTEyMjA4MjIxMlowDTELMAkGA1UEBhMCTkwwOTATBgcqhkjOPQIBBggqhkjOPQMBBwMiAALcD1XzKepFxWMAOqV+ln1fybBt7DRO5CV0f9A6mRp2xaMwMC4wLAYDVR0RBCUwI4IhMDRkNy0yMTctMTIzLTE4LTI2Lm5ncm9rLWZyZWUuYXBwMAoGCCqGSM49BAMCA0kAMEYCIQDWjkAm/iLhGWcgKILW48f43vEUByvJd2R4lxdTdK9w+wIhALcZIgrH2h9SoXHjuI9ktOMbfVHxt59iq+lOKsC4yOUQ',
'MIIBJzCBz6ADAgECAhA0WLLsSm0Hf5R2/q7neHUKMAoGCCqGSM49BAMCMA0xCzAJBgNVBAYTAk5MMB4XDTcwMDEwMTAwMDAwMFoXDTI1MTEyMjA4MjIxMlowDTELMAkGA1UEBhMCTkwwOTATBgcqhkjOPQIBBggqhkjOPQMBBwMiAALcD1XzKepFxWMAOqV+ln1fybBt7DRO5CV0f9A6mRp2xaMxMC8wLQYDVR0RBCYwJIIiNGFjNS0xMDktMzctMTUwLTE5OC5uZ3Jvay1mcmVlLmFwcDAKBggqhkjOPQQDAgNHADBEAiAEqDL6WHBelM4YW3L0k2criU+Za/FlDEuAJKuY+LiY/AIgR0qGuW9qu4wUo/kcJ75mv+jAwV25ABmYAnbUX/7u5lI=',
]

// https://gitlab.opencode.de/bmi/eudi-wallet/eidas-2.0-architekturkonzept/-/blob/main/architecture-proposal.md#pid-contents
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,14 @@ export function FunkeCredentialNotificationScreen() {
// TODO: where to transform?
// Combine oid4vci issuer metadata and openid fed into one pipeline. If openid it's trusted
const issuerMetadata = resolvedCredentialOffer?.metadata.credentialIssuer
const configuration =
resolvedCredentialOffer?.offeredCredentialConfigurations[
// TODO: handle empty configuration ids
resolvedCredentialOffer.credentialOfferPayload.credential_configuration_ids[0]
]
// We want the first supported configuration id
// TODO: handle empty configuration ids
const configurationId = resolvedCredentialOffer?.offeredCredentialConfigurations
? Object.keys(resolvedCredentialOffer.offeredCredentialConfigurations)[0]
: undefined
const configuration = configurationId
? resolvedCredentialOffer?.offeredCredentialConfigurations[configurationId]
: undefined

const credentialDisplay = getCredentialDisplayWithDefaults(
configuration && issuerMetadata
Expand Down Expand Up @@ -122,11 +125,13 @@ export function FunkeCredentialNotificationScreen() {
async (
resolvedCredentialOffer: OpenId4VciResolvedCredentialOffer,
tokenResponse: OpenId4VciRequestTokenResponse,
configurationId: string,
resolvedAuthorizationRequest?: OpenId4VciResolvedAuthorizationRequest
) => {
const credentialResponses = await receiveCredentialFromOpenId4VciOffer({
agent,
resolvedCredentialOffer,
credentialConfigurationIdsToRequest: [configurationId],
accessToken: tokenResponse,
clientId: resolvedAuthorizationRequest ? authorization.clientId : undefined,
})
Expand Down Expand Up @@ -164,7 +169,7 @@ export function FunkeCredentialNotificationScreen() {

const acquireCredentialsAuth = useCallback(
async (authorizationCode: string) => {
if (!resolvedCredentialOffer || !resolvedAuthorizationRequest) {
if (!resolvedCredentialOffer || !resolvedAuthorizationRequest || !configurationId) {
setErrorReason('Credential information could not be extracted')
return
}
Expand All @@ -179,20 +184,20 @@ export function FunkeCredentialNotificationScreen() {
'codeVerifier' in resolvedAuthorizationRequest ? resolvedAuthorizationRequest.codeVerifier : undefined,
})

await retrieveCredentials(resolvedCredentialOffer, tokenResponse, resolvedAuthorizationRequest)
await retrieveCredentials(resolvedCredentialOffer, tokenResponse, configurationId, resolvedAuthorizationRequest)
} catch (error) {
agent.config.logger.error(`Couldn't receive credential from OpenID4VCI offer`, {
error,
})
setErrorReason('Error while retrieving credentials')
}
},
[resolvedCredentialOffer, resolvedAuthorizationRequest, retrieveCredentials, agent]
[resolvedCredentialOffer, resolvedAuthorizationRequest, retrieveCredentials, agent, configurationId]
)

const acquireCredentialsPreAuth = useCallback(
async (txCode?: string) => {
if (!resolvedCredentialOffer) {
if (!resolvedCredentialOffer || !configurationId) {
setErrorReason('Credential information could not be extracted')
return
}
Expand All @@ -203,23 +208,22 @@ export function FunkeCredentialNotificationScreen() {
resolvedCredentialOffer,
txCode,
})
await retrieveCredentials(resolvedCredentialOffer, tokenResponse)
await retrieveCredentials(resolvedCredentialOffer, tokenResponse, configurationId)
} catch (error) {
agent.config.logger.error(`Couldn't receive credential from OpenID4VCI offer`, {
error,
})
setErrorReason('Error while retrieving credentials')
}
},
[resolvedCredentialOffer, agent, retrieveCredentials]
[resolvedCredentialOffer, agent, retrieveCredentials, configurationId]
)

const parsePresentationRequestUrl = useCallback(
(oid4vpRequestUrl: string) =>
getCredentialsForProofRequest({
agent,
uri: oid4vpRequestUrl,
allowUntrustedCertificates: true,
})
.then(setCredentialsForRequest)
.catch((error) => {
Expand Down Expand Up @@ -276,7 +280,6 @@ export function FunkeCredentialNotificationScreen() {
agent,
resolvedRequest: credentialsForRequest,
selectedCredentials: {},
allowUntrustedCertificate: true,
})

const { authorizationCode } = await acquireAuthorizationCodeUsingPresentation({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export function FunkeOpenIdPresentationNotificationScreen() {
agent,
data: params.data,
uri: params.uri,
allowUntrustedCertificates: true,
})
.then(setCredentialsForRequest)
.catch((error) => {
Expand Down Expand Up @@ -93,7 +92,6 @@ export function FunkeOpenIdPresentationNotificationScreen() {
agent,
resolvedRequest: credentialsForRequest,
selectedCredentials: {},
allowUntrustedCertificate: true,
})

await addSharedActivityForCredentialsForRequest(agent, credentialsForRequest, 'success')
Expand Down
1 change: 1 addition & 0 deletions apps/easypid/src/utils/resetWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { removeShouldUseCloudHsm } from '../features/onboarding/useShouldUseCloudHsm'

export async function resetWallet(secureUnlock: SecureUnlockReturn<SecureUnlockContext>) {
console.log('Resetting wallet')
if (secureUnlock.state === 'unlocked') {
const agent = secureUnlock.context.agent
secureUnlock.lock()
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
"@credo-ts/indy-vdr": "catalog:",
"@credo-ts/openid4vc": "catalog:",
"@credo-ts/question-answer": "catalog:",
"@credo-ts/react-native": "catalog:"
"@credo-ts/react-native": "catalog:",
"@sphereon/pex-models": "catalog:"
},
"patchedDependencies": {
"@hyperledger/[email protected]": "patches/@[email protected]",
Expand Down
41 changes: 36 additions & 5 deletions packages/agent/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
Agent,
AutoAcceptCredential,
AutoAcceptProof,
ClaimFormat,
ConnectionsModule,
CredentialsModule,
DidsModule,
Expand All @@ -23,13 +24,15 @@ import {
KeyDidRegistrar,
KeyDidResolver,
LogLevel,
Mdoc,
MediationRecipientModule,
MediatorPickupStrategy,
ProofsModule,
V2CredentialProtocol,
V2ProofProtocol,
WebDidResolver,
WsOutboundTransport,
X509Module,
} from '@credo-ts/core'
import {
IndyVdrAnonCredsRegistry,
Expand All @@ -45,6 +48,7 @@ import { ariesAskar } from '@hyperledger/aries-askar-react-native'
import { indyVdr } from '@hyperledger/indy-vdr-react-native'
import { DidWebAnonCredsRegistry } from 'credo-ts-didweb-anoncreds'

import { bdrPidIssuerCertificate, pidSchemes } from '../../../apps/easypid/src/constants'
import { indyNetworks } from './indyNetworks'
import { appLogger } from './logger'

Expand Down Expand Up @@ -80,16 +84,43 @@ export const initializeEasyPIDAgent = async ({
modules: {
ariesAskar: askarModule,
openId4VcHolder: new OpenId4VcHolderModule(),
x509: new X509Module({
getTrustedCertificatesForVerification: (agentContext, { certificateChain, verification }) => {
if (verification.type === 'credential') {
// Only allow BDR certificate for PID credentials for now
if (
verification.credential instanceof Mdoc &&
pidSchemes.msoMdocDoctypes.includes(verification.credential.docType)
) {
return [bdrPidIssuerCertificate]
}

if (
verification.credential.claimFormat === ClaimFormat.SdJwtVc &&
pidSchemes.sdJwtVcVcts.includes(verification.credential.payload.vct as string)
) {
return [bdrPidIssuerCertificate]
}

// If not PID, we allow any certificate for now
return [certificateChain[0].toString('pem')]
}

// Allow any actor for auth requests for now
if (verification.type === 'oauth2SecuredAuthorizationRequest') {
return [certificateChain[0].toString('pem')]
}

return undefined
},
trustedCertificates:
trustedX509Certificates.length > 0 ? (trustedX509Certificates as [string, ...string[]]) : undefined,
}),
},
})

await agent.initialize()

// Register the trusted x509 certificates
for (const trustedCertificate of trustedX509Certificates) {
agent.x509.addTrustedCertificate(trustedCertificate)
}

return agent
}

Expand Down
51 changes: 15 additions & 36 deletions packages/agent/src/invitation/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,25 +398,15 @@ export const getCredentialsForProofRequest = async ({
agent,
data,
uri,
allowUntrustedCertificates = false,
}: {
agent: EitherAgent
// Either data or uri can be provided
data?: string
uri?: string
allowUntrustedCertificates?: boolean
}) => {
let requestUri: string

const { certificate = null, data: newData = null } = allowUntrustedCertificates
? await extractCertificateFromAuthorizationRequest({ data, uri })
: {}

if (newData) {
// FIXME: Credo only support request string, but we already parsed it before. So we construct an request here
// but in the future we need to support the parsed request in Credo directly
requestUri = `openid://?request=${encodeURIComponent(newData)}`
} else if (data) {
if (data) {
// FIXME: Credo only support request string, but we already parsed it before. So we construct an request here
// but in the future we need to support the parsed request in Credo directly
requestUri = `openid://?request=${encodeURIComponent(data)}`
Expand All @@ -432,10 +422,7 @@ export const getCredentialsForProofRequest = async ({
requestUri,
})

// Temp solution to add and remove the trusted certificate
const resolved = await withTrustedCertificate(agent, certificate, () =>
agent.modules.openId4VcHolder.resolveSiopAuthorizationRequest(requestUri)
)
const resolved = await agent.modules.openId4VcHolder.resolveSiopAuthorizationRequest(requestUri)

let formattedSubmission: FormattedSubmission
if (resolved.presentationExchange) {
Expand Down Expand Up @@ -481,12 +468,10 @@ export const shareProof = async ({
agent,
resolvedRequest,
selectedCredentials,
allowUntrustedCertificate = false,
}: {
agent: EitherAgent
resolvedRequest: CredentialsForProofRequest
selectedCredentials: { [inputDescriptorId: string]: string }
allowUntrustedCertificate?: boolean
}) => {
const { authorizationRequest } = resolvedRequest
if (
Expand Down Expand Up @@ -525,25 +510,19 @@ export const shareProof = async ({
: undefined

try {
// Temp solution to add and remove the trusted certificate
const certificate =
authorizationRequest.jwt && allowUntrustedCertificate ? extractCertificateFromJwt(authorizationRequest.jwt) : null

const result = await withTrustedCertificate(agent, certificate, () =>
agent.modules.openId4VcHolder.acceptSiopAuthorizationRequest({
authorizationRequest,
presentationExchange: presentationExchangeCredentials
? {
credentials: presentationExchangeCredentials,
}
: undefined,
dcql: dcqlCredentials
? {
credentials: dcqlCredentials,
}
: undefined,
})
)
const result = await agent.modules.openId4VcHolder.acceptSiopAuthorizationRequest({
authorizationRequest,
presentationExchange: presentationExchangeCredentials
? {
credentials: presentationExchangeCredentials,
}
: undefined,
dcql: dcqlCredentials
? {
credentials: dcqlCredentials,
}
: undefined,
})

// if redirect_uri is provided, open it in the browser
// Even if the response returned an error, we must open this uri
Expand Down
Loading
Loading