From 86fbfde37ff049deb20f55273541ad32a28f9f7c Mon Sep 17 00:00:00 2001 From: "A.G.J. Cate" Date: Sat, 8 Jun 2024 11:28:11 +0200 Subject: [PATCH 1/2] feat: added SDJwtPlugin to agent --- package.json | 1 + src/agent/index.ts | 8 +++++++ src/services/signatureService.ts | 13 +++++++++-- src/utils/CryptoUtils.ts | 37 ++++++++++++++++++++++++++++++++ src/utils/index.ts | 1 + yarn.lock | 31 ++++++++++++++++++++++++-- 6 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 src/utils/CryptoUtils.ts diff --git a/package.json b/package.json index d31f822c..8f93bcef 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "@sphereon/ssi-sdk.siopv2-oid4vp-op-auth": "0.23.5-next.22", "@sphereon/ssi-sdk.vc-handler-ld-local": "0.23.5-next.22", "@sphereon/ssi-sdk.xstate-machine-persistence": "0.23.5-next.22", + "@sphereon/ssi-sdk.sd-jwt": "0.24.1-unstable.47", "@sphereon/ssi-types": "0.23.5-next.22", "@sphereon/ui-components.core": "0.2.0", "@sphereon/ui-components.ssi-react-native": "0.2.0", diff --git a/src/agent/index.ts b/src/agent/index.ts index 9e10f2ac..eee1a794 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -20,6 +20,7 @@ import { SphereonJsonWebSignature2020, } from '@sphereon/ssi-sdk.vc-handler-ld-local'; import {MachineStatePersistence, MachineStatePersistEventType} from '@sphereon/ssi-sdk.xstate-machine-persistence'; +import {SDJwtPlugin} from '@sphereon/ssi-sdk.sd-jwt'; import {createAgent, IAgentPlugin} from '@veramo/core'; import {CredentialPlugin} from '@veramo/credential-w3c'; import {DataStore, DataStoreORM, DIDStore, KeyStore, PrivateKeyStore} from '@veramo/data-store'; @@ -38,8 +39,10 @@ import {DB_CONNECTION_NAME, DB_ENCRYPTION_KEY} from '../@config/database'; import {addLinkListeners} from '../handlers/LinkHandlers'; import {getDbConnection} from '../services/databaseService'; import {dispatchIdentifier} from '../services/identityService'; +import {verifySDJWTSignature} from '../services/signatureService'; import store from '../store'; import {dispatchVerifiableCredential} from '../store/actions/credential.actions'; +import {generateSalt, generateDigest} from '../utils'; import {ADD_IDENTITY_SUCCESS} from '../types/store/contact.action.types'; import {KeyManagementSystemEnum, SupportedDidMethodEnum, TAgentTypes} from '../types'; @@ -139,6 +142,11 @@ const agentPlugins: Array = [ eventTypes: [LinkHandlerEventType.LINK_HANDLER_URL], handlers: linkHandlers, }), + new SDJwtPlugin({ + hasher: generateDigest, + saltGenerator: generateSalt, + verifySignature: verifySDJWTSignature, + }), ]; const agent = createAgent({ diff --git a/src/services/signatureService.ts b/src/services/signatureService.ts index d507583f..4a71c236 100644 --- a/src/services/signatureService.ts +++ b/src/services/signatureService.ts @@ -1,9 +1,8 @@ import {IIdentifier} from '@veramo/core'; import {createJWT, Signer} from 'did-jwt'; - import {keyManagerSign} from '../agent'; -import {ISignJwtArgs} from '../types'; import {signatureAlgorithmFromKey} from '../utils'; +import {ISignJwtArgs} from '../types'; export const signJWT = async (args: ISignJwtArgs): Promise => { const options = { @@ -30,3 +29,13 @@ const getSigner = (identifier: IIdentifier): Signer => { }); }; }; + +export const verifySDJWTSignature = async (data: string, signature: string, key: JsonWebKey): Promise>> => { + let {alg, crv} = key; + if (alg === 'ES256') alg = 'ECDSA'; + const publicKey = await crypto.subtle.importKey('jwk', key, {name: alg, namedCurve: crv} as EcKeyImportParams, true, ['verify']); + + return Promise.resolve( + crypto.subtle.verify({name: alg as string, hash: 'SHA-256'}, publicKey, Buffer.from(signature, 'base64'), Buffer.from(data)), + ); +}; diff --git a/src/utils/CryptoUtils.ts b/src/utils/CryptoUtils.ts new file mode 100644 index 00000000..7c31ceb9 --- /dev/null +++ b/src/utils/CryptoUtils.ts @@ -0,0 +1,37 @@ +import {CryptoDigestAlgorithm, digest, randomUUID} from 'expo-crypto'; + +export const generateDigest = async (data: string, algorithm: string): Promise => { + const cryptoDigestAlgorithm = getCryptoDigestAlgorithm(algorithm); + const bufferSource = await BufferSourceFrom(data); + + return new Uint8Array(await digest(cryptoDigestAlgorithm, bufferSource)); +}; + +export const generateSalt = async (): Promise => { + return randomUUID(); +}; + +export const BufferSourceFrom = async (data: string): Promise => { + return new TextEncoder().encode(data); +}; + +export const getCryptoDigestAlgorithm = (algorithm: string): CryptoDigestAlgorithm => { + switch (algorithm.toUpperCase()) { + case 'SHA-256': + return CryptoDigestAlgorithm.SHA256; + case 'SHA1': + return CryptoDigestAlgorithm.SHA1; + case 'SHA384': + return CryptoDigestAlgorithm.SHA384; + case 'SHA512': + return CryptoDigestAlgorithm.SHA512; + case 'MD2': + return CryptoDigestAlgorithm.MD2; + case 'MD4': + return CryptoDigestAlgorithm.MD4; + case 'MD5': + return CryptoDigestAlgorithm.MD5; + default: + throw new Error(`crypto algorithm: ${algorithm} not supported`); + } +}; diff --git a/src/utils/index.ts b/src/utils/index.ts index 647cef06..9e59a20d 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,6 +1,7 @@ export * from './AppUtils'; export * from './BiometricUtils'; export * from './CredentialUtils'; +export * from './CryptoUtils'; export * from './DateUtils'; export * from './DeeplinkUtils'; export * from './ImageUtils'; diff --git a/yarn.lock b/yarn.lock index ef57f1c9..f784fa96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3144,6 +3144,16 @@ "@noble/hashes" "~1.3.2" "@scure/base" "~1.1.4" +"@sd-jwt/core@0.6.1", "@sd-jwt/core@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.6.1.tgz#d28be10d0f4b672636fcf7ad71737cb08e5dae96" + integrity sha512-egFTb23o6BGWF93vnjopN02rSiC1HOOnkk9BI8Kao3jz9ipZAHdO6wF7gwfZm5Nlol/Kd1/KSLhbOUPYt++FjA== + dependencies: + "@sd-jwt/decode" "0.6.1" + "@sd-jwt/present" "0.6.1" + "@sd-jwt/types" "0.6.1" + "@sd-jwt/utils" "0.6.1" + "@sd-jwt/decode@0.6.1", "@sd-jwt/decode@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.6.1.tgz#141f7782df53bab7159a75d91ed4711e1c14a7ea" @@ -3152,7 +3162,7 @@ "@sd-jwt/types" "0.6.1" "@sd-jwt/utils" "0.6.1" -"@sd-jwt/present@^0.6.1": +"@sd-jwt/present@0.6.1", "@sd-jwt/present@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.6.1.tgz#82b9188becb0fa240897c397d84a54d55c7d169e" integrity sha512-QRD3TUDLj4PqQNZ70bBxh8FLLrOE9mY8V9qiZrJSsaDOLFs2p1CtZG+v9ig62fxFYJZMf4bWKwYjz+qqGAtxCg== @@ -3161,6 +3171,13 @@ "@sd-jwt/types" "0.6.1" "@sd-jwt/utils" "0.6.1" +"@sd-jwt/sd-jwt-vc@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/sd-jwt-vc/-/sd-jwt-vc-0.6.1.tgz#2493aeb92a9354d9ae5e0de57d75a806fe8af90b" + integrity sha512-eF7NAFvedBCx+vrw4TVY3evUz5rAG8/FtB/CUudYEigKcpanLgfuNGhk93D45k+lLDG0b24w+qorqbpLZzHA2g== + dependencies: + "@sd-jwt/core" "0.6.1" + "@sd-jwt/types@0.6.1", "@sd-jwt/types@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.6.1.tgz#fc4235e00cf40d35a21d6bc02e44e12d7162aa9b" @@ -3443,7 +3460,7 @@ varint "^6.0.0" web-encoding "^1.1.5" -"@sphereon/ssi-sdk-ext.did-utils@0.18.0", "@sphereon/ssi-sdk-ext.did-utils@0.19.0": +"@sphereon/ssi-sdk-ext.did-utils@0.18.0", "@sphereon/ssi-sdk-ext.did-utils@0.19.0", "@sphereon/ssi-sdk-ext.did-utils@0.19.1-next.48": version "0.19.0" resolved "https://registry.yarnpkg.com/@sphereon/ssi-sdk-ext.did-utils/-/ssi-sdk-ext.did-utils-0.19.0.tgz#2ecb6a3e5d7faea62cbde76bb12e13a7954542d2" integrity sha512-RCKayfL/+CEF7/c5PgxwgizQrU5jmb+nS9Ok9ML6dFRdzWYkQjknh9844Kxi40+xXkEuyhwrlPdD5mLz2Gr6Kw== @@ -3617,6 +3634,16 @@ "@sphereon/ssi-types" "0.23.5-next.22+b977f3cb" "@veramo/core" "4.2.0" +"@sphereon/ssi-sdk.sd-jwt@0.24.1-unstable.47": + version "0.24.1-unstable.47" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-sdk.sd-jwt/-/ssi-sdk.sd-jwt-0.24.1-unstable.47.tgz#e824482ba8f8712ada1902f913634fa076000719" + integrity sha512-ijYxLc2A9mU3/rGuPN6FYzlKI1JN9whEs+OvbyxtycSx4NH210sE/HThnAFhxg7qRIipiDXDxBhuwMWOf72isg== + dependencies: + "@sd-jwt/core" "^0.6.1" + "@sd-jwt/sd-jwt-vc" "^0.6.1" + "@sphereon/ssi-sdk-ext.did-utils" "0.19.1-next.48" + "@veramo/utils" "4.2.0" + "@sphereon/ssi-sdk.siopv2-oid4vp-op-auth@0.23.5-next.22": version "0.23.5-next.22" resolved "https://registry.yarnpkg.com/@sphereon/ssi-sdk.siopv2-oid4vp-op-auth/-/ssi-sdk.siopv2-oid4vp-op-auth-0.23.5-next.22.tgz#0046bbedf8ab3e2d23869c014c4cf0eec6135fb1" From 30624f9b5d1f4997aa8c881baedcf4ecf8bdfd5e Mon Sep 17 00:00:00 2001 From: "A.G.J. Cate" Date: Sun, 9 Jun 2024 23:41:11 +0200 Subject: [PATCH 2/2] chore: added sd-jwt plugin to agent types --- src/types/agent/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/types/agent/index.ts b/src/types/agent/index.ts index 07d0eda4..9e7f31c4 100644 --- a/src/types/agent/index.ts +++ b/src/types/agent/index.ts @@ -6,6 +6,7 @@ import {ICredentialHandlerLDLocal} from '@sphereon/ssi-sdk.vc-handler-ld-local'; import {IIssuanceBranding} from '@sphereon/ssi-sdk.issuance-branding'; import {IOID4VCIHolder} from '@sphereon/ssi-sdk.oid4vci-holder'; import {IMachineStatePersistence} from '@sphereon/ssi-sdk.xstate-machine-persistence'; +import {ISDJwtPlugin} from '@sphereon/ssi-sdk.sd-jwt'; export type TAgentTypes = IDIDManager & IKeyManager & @@ -19,4 +20,5 @@ export type TAgentTypes = IDIDManager & ICredentialHandlerLDLocal & IIssuanceBranding & IOID4VCIHolder & - IMachineStatePersistence; + IMachineStatePersistence & + ISDJwtPlugin;