Skip to content

Commit

Permalink
feat: use veramo packages (#170)
Browse files Browse the repository at this point in the history
* chore: remove old website (#136)

* chore: removed workflows, website

* chore: update lock file and some other fixes

* feat: update packages to esm (#135)

* feat: esm ssi-snap-types

* feat: esm connector

* fix: add readme to output

* chore: update connector deps

* chore: lintstaged.cjs

* chore: more lintstaged.cjs

* chore: connector lintstaged.cjs

* feat: esm vcmanager and update configs

* feat: esm utils

* feat: update tests

* chore: deps

* fix: build dapp

* fix: docs

* fix: fixes config issues

* fix: fixes config issues

* fix: resolve last issues

* feat: replaced get currentAccount func

* chore: update dev deps (#144)

* chore: update dev deps

* fix: fixes lockfile

* fix: fixes lockfile

* chore: update nodejs and pnpm

* chore: remove patch

* chore: add back snap types patch

* fix: should fix CI workflow

* fix: set pnpm version in main CI

* fix: try fixing symlink issues

* fix: tests for new getCurrentAddress

* feat: add did:jwk provider from veramo

* feat: replace did jwk and pkh creation

* chore: update pnpm-lock.yaml

* fix: post rebase fixes

* fix: dependencies issue

* fix: tests and params objects

* fix: update metamask snaps-types patch

* fix: update some code and a lot of tests

---------

Co-authored-by: Tadej <[email protected]>
  • Loading branch information
martines3000 and tadejpodrekar authored Apr 20, 2023
1 parent 7fc29dc commit d1fc4fe
Show file tree
Hide file tree
Showing 36 changed files with 1,592 additions and 1,160 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,4 @@ nx-cloud.env
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
snap/patches/cross-fetch+3.1.5.patch
.vscode
6 changes: 0 additions & 6 deletions .vscode/settings.json

This file was deleted.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"pnpm": {
"patchedDependencies": {
"[email protected]": "patches/[email protected]",
"@metamask/snaps-types@0.28.0": "patches/@metamask__snaps-types@0.28.0.patch"
"@metamask/snaps-types@0.30.0": "patches/@metamask__snaps-types@0.30.0.patch"
},
"allowNonAppliedPatches": true
}
Expand Down
1 change: 1 addition & 0 deletions packages/snap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"@veramo/credential-w3c": "5.1.2",
"@veramo/did-manager": "5.1.2",
"@veramo/did-provider-ethr": "5.1.2",
"@veramo/did-provider-jwk": "5.1.2",
"@veramo/did-provider-pkh": "5.1.2",
"@veramo/did-resolver": "5.1.2",
"@veramo/key-manager": "5.1.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/snap/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/blockchain-lab-um/ssi-snap.git"
},
"source": {
"shasum": "wSNcy4Yo6JXRQYtKxGPtnFievIt7EY8eTVOQylsNU3E=",
"shasum": "EgkVyISZyClGjdbalbuH/mmqt6tYjSLDseaDnKItOV4=",
"location": {
"npm": {
"filePath": "dist/snap.js",
Expand Down
159 changes: 159 additions & 0 deletions packages/snap/src/did/jwk/jwkDidUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import {
JwkDidSupportedKeyTypes,
KeyUse,
SupportedKeyTypes,
} from '@veramo/did-provider-jwk';
import {
bytesToBase64url,
encodeJoseBlob,
extractPublicKeyHex,
hexToBytes,
} from '@veramo/utils';
import type { JsonWebKey, VerificationMethod } from 'did-resolver';
import elliptic from 'elliptic';

import { SSISnapState } from '../../interfaces';
import type { Agent } from '../../veramo/setup';

const EC = elliptic.ec;

export function getKeyUse(
keyType: JwkDidSupportedKeyTypes,
passedKeyUse?: KeyUse
): KeyUse {
if (passedKeyUse) {
if (passedKeyUse !== 'sig' && passedKeyUse !== 'enc') {
throw new Error('illegal_argument: Key use must be sig or enc');
}
if (passedKeyUse === 'sig' && keyType === 'X25519') {
throw new Error(
'illegal_argument: X25519 keys cannot be used for signing'
);
}
if (passedKeyUse === 'enc' && keyType === 'Ed25519') {
throw new Error(
'illegal_argument: Ed25519 keys cannot be used for encryption'
);
}
return passedKeyUse;
}
switch (keyType) {
case 'Secp256k1':
case 'Secp256r1':
case 'Ed25519':
return 'sig';
case 'X25519':
return 'enc';
default:
throw new Error('illegal_argument: Unknown key type');
}
}

export function isJWK(data: unknown): data is JsonWebKey {
if (
typeof data === 'object' &&
data &&
'crv' in data &&
typeof data.crv === 'string' &&
'kty' in data &&
'x' in data &&
typeof data.x === 'string' &&
((data.kty === 'EC' && 'y' in data && typeof data.y === 'string') ||
(data.kty === 'OKP' && !('y' in data)))
) {
return true;
}
return false;
}

function createJWK(
keyType: JwkDidSupportedKeyTypes,
pubKey: string | Uint8Array,
passedKeyUse?: KeyUse
): JsonWebKey | undefined {
const keyUse = getKeyUse(keyType, passedKeyUse);
switch (keyType) {
case SupportedKeyTypes.Secp256k1: {
const ctx = new EC('secp256k1');
const pubPoint = ctx.keyFromPublic(pubKey, 'hex').getPublic();
const x = pubPoint.getX();
const y = pubPoint.getY();

return {
alg: 'ES256K',
crv: 'secp256k1',
kty: 'EC',
...(keyUse && { use: keyUse }),
x: bytesToBase64url(hexToBytes(x.toString('hex'))),
y: bytesToBase64url(hexToBytes(y.toString('hex'))),
} as JsonWebKey;
}
case SupportedKeyTypes.Secp256r1: {
const ctx = new EC('p256');
const pubPoint = ctx.keyFromPublic(pubKey, 'hex').getPublic();
const x = pubPoint.getX();
const y = pubPoint.getY();

return {
alg: 'ES256',
crv: 'P-256',
kty: 'EC',
...(keyUse && { use: keyUse }),
x: bytesToBase64url(hexToBytes(x.toString('hex'))),
y: bytesToBase64url(hexToBytes(y.toString('hex'))),
} as JsonWebKey;
}
case SupportedKeyTypes.Ed25519:
return {
alg: 'EdDSA',
crv: 'Ed25519',
kty: 'OKP',
...(keyUse && { use: keyUse }),
x: bytesToBase64url(
typeof pubKey === 'string' ? hexToBytes(pubKey) : pubKey
),
} as JsonWebKey;
case SupportedKeyTypes.X25519:
return {
alg: 'ECDH-ES',
crv: 'X25519',
kty: 'OKP',
...(keyUse && { use: keyUse }),
x: bytesToBase64url(
typeof pubKey === 'string' ? hexToBytes(pubKey) : pubKey
),
} as JsonWebKey;
default:
throw new Error(`not_supported: Failed to create JWK using ${keyType}`);
}
}

export function generateJwkFromVerificationMethod(
keyType: JwkDidSupportedKeyTypes,
key: VerificationMethod,
keyUse?: KeyUse
) {
return createJWK(keyType, extractPublicKeyHex(key), keyUse);
}

export async function getDidJwkIdentifier(
state: SSISnapState,
account: string,
agent?: Agent
): Promise<string> {
if (agent) {
const identifier = await agent.didManagerCreate({
provider: 'did:jwk',
options: {
keyType: 'Secp256k1',
},
});
return identifier.did;
}
const { publicKey } = state.accountState[account];
const jwk = generateJwkFromVerificationMethod('Secp256k1', {
publicKeyHex: publicKey,
} as VerificationMethod);
const jwkBase64url = encodeJoseBlob(jwk as object);
return `did:jwk:${jwkBase64url}`;
}
1 change: 0 additions & 1 deletion packages/snap/src/did/key/keyDidProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ export class KeyDIDProvider extends AbstractIdentifierProvider {
identifier: IIdentifier,
context: IContext
): Promise<boolean> {
// eslint-disable-next-line no-restricted-syntax
for (const { kid } of identifier.keys) {
// eslint-disable-next-line no-await-in-loop
await context.agent.keyManagerDelete({ kid });
Expand Down
16 changes: 7 additions & 9 deletions packages/snap/src/did/key/keyDidResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,19 @@ import {
Resolver,
} from 'did-resolver';

import { getCurrentAccount, getPublicKey } from '../../utils/snapUtils';
import { getCurrentAccount } from '../../utils/snapUtils';
import { getSnapState } from '../../utils/stateUtils';

// FIXME: We also shouldn't use account here and extract te public key from the did
export const resolveSecp256k1 = async (
snap: SnapsGlobalObject,
account: string,
did: string
): Promise<DIDDocument> => {
const state = await getSnapState(snap);
const publicKey = await getPublicKey({
snap,
state,
account,
ethereum,
origin: '',
});

// FIXME: This is wrong (previously was getPublicKey -> which is also wrong)
const { publicKey } = state.accountState[account];

// TODO: Change id ?
const didDocument: DIDDocument = {
Expand Down Expand Up @@ -81,7 +78,8 @@ export const resolveDidKey: DIDResolver = async (
options: DIDResolutionOptions
): Promise<DIDResolutionResult> => {
try {
const account = await getCurrentAccount(ethereum);
const state = await getSnapState(snap);
const account = getCurrentAccount(state);
const startsWith = parsed.did.substring(0, 12);
if (startsWithMap[startsWith] !== undefined) {
const didDocument = await startsWithMap[startsWith](
Expand Down
17 changes: 15 additions & 2 deletions packages/snap/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { getDid } from './rpc/did/getDID';
import { resolveDID } from './rpc/did/resolveDID';
import { switchMethod } from './rpc/did/switchMethod';
import { togglePopups } from './rpc/snap/configure';
import { setCurrentAccount } from './rpc/snap/setCurrentAccount';
import { createVC } from './rpc/vc/createVC';
import { createVP } from './rpc/vc/createVP';
import { deleteVC } from './rpc/vc/deleteVC';
Expand All @@ -23,6 +24,7 @@ import {
isValidQueryRequest,
isValidResolveDIDRequest,
isValidSaveVCRequest,
isValidSetCurrentAccountRequest,
isValidSetVCStoreRequest,
isValidSwitchMethodRequest,
isValidVerifyDataRequest,
Expand All @@ -42,7 +44,18 @@ export const onRpcRequest: OnRpcRequestHandler = async ({
let state = await getSnapStateUnchecked(snap);
if (state === null) state = await initSnapState(snap);

const account = await getCurrentAccount(ethereum);
let res;

if (request.method === 'setCurrentAccount') {
isValidSetCurrentAccountRequest(request.params);
res = await setCurrentAccount(
{ state, snap, ethereum, account: '', origin },
request.params
);
return ResultObject.success(res);
}

const account = getCurrentAccount(state);

const apiParams: ApiParams = {
state,
Expand All @@ -57,7 +70,6 @@ export const onRpcRequest: OnRpcRequestHandler = async ({
apiParams.bip44CoinTypeNode = await getAddressKeyDeriver(apiParams);
await setAccountPublicKey(apiParams);
}
let res;
switch (request.method) {
case 'queryVCs':
isValidQueryRequest(request.params, apiParams.account, apiParams.state);
Expand Down Expand Up @@ -98,6 +110,7 @@ export const onRpcRequest: OnRpcRequestHandler = async ({
res = await switchMethod(apiParams, request.params);
return ResultObject.success(res);
case 'getDID':
apiParams.bip44CoinTypeNode = await getAddressKeyDeriver(apiParams);
res = await getDid(apiParams);
return ResultObject.success(res);
case 'getSelectedMethod':
Expand Down
5 changes: 4 additions & 1 deletion packages/snap/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ export type SSISnapState = {
* Account specific storage
*/
accountState: Record<string, SSIAccountState>;

/**
* Current account
*/
currentAccount: string;
/**
* Configuration for SSISnap
*/
Expand Down
21 changes: 10 additions & 11 deletions packages/snap/src/rpc/did/getDID.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { MetaMaskInpageProvider } from '@metamask/providers';
import { SnapsGlobalObject } from '@metamask/snaps-types';
import { BIP44CoinTypeNode } from '@metamask/key-tree';

import { SSISnapState } from '../../interfaces';
import { ApiParams } from '../../interfaces';
import { getCurrentDid } from '../../utils/didUtils';

export async function getDid(params: {
state: SSISnapState;
snap: SnapsGlobalObject;
account: string;
ethereum: MetaMaskInpageProvider;
}): Promise<string> {
const res = await getCurrentDid(params);
return res;
export async function getDid(params: ApiParams): Promise<string> {
return getCurrentDid({
ethereum: params.ethereum,
snap: params.snap,
state: params.state,
account: params.account,
bip44CoinTypeNode: params.bip44CoinTypeNode as BIP44CoinTypeNode,
});
}
11 changes: 2 additions & 9 deletions packages/snap/src/rpc/did/resolveDID.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import { MetaMaskInpageProvider } from '@metamask/providers';
import { SnapsGlobalObject } from '@metamask/snaps-types';
import { ApiParams } from 'src/interfaces';

import { resolveDid } from '../../utils/didUtils';

export async function resolveDID(
params: {
snap: SnapsGlobalObject;
ethereum: MetaMaskInpageProvider;
},
did: string
) {
export async function resolveDID(params: ApiParams, did: string) {
if (did === '') return { message: 'DID is empty' };
const res = await resolveDid({ ...params, did });
return res;
Expand Down
12 changes: 10 additions & 2 deletions packages/snap/src/rpc/did/switchMethod.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SwitchMethodRequestParams } from '@blockchain-lab-um/ssi-snap-types';
import { BIP44CoinTypeNode } from '@metamask/key-tree';
import { divider, heading, panel, text } from '@metamask/snaps-ui';

import { ApiParams } from '../../interfaces';
Expand All @@ -9,7 +10,7 @@ export async function switchMethod(
params: ApiParams,
{ didMethod }: SwitchMethodRequestParams
): Promise<string> {
const { state, snap, account } = params;
const { state, snap, ethereum, account, bip44CoinTypeNode } = params;
const method = state.accountState[account].accountConfig.ssi.didMethod;
if (didMethod !== method) {
const content = panel([
Expand All @@ -20,7 +21,14 @@ export async function switchMethod(
]);

if (await snapConfirm(snap, content)) {
const res = await changeCurrentMethod({ ...params, didMethod });
const res = await changeCurrentMethod({
snap,
ethereum,
state,
account,
didMethod,
bip44CoinTypeNode: bip44CoinTypeNode as BIP44CoinTypeNode,
});
return res;
}

Expand Down
15 changes: 15 additions & 0 deletions packages/snap/src/rpc/snap/setCurrentAccount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { SetCurrentAccountRequestParams } from '@blockchain-lab-um/ssi-snap-types';

import { ApiParams } from '../../interfaces';
import { updateSnapState } from '../../utils/stateUtils';

export async function setCurrentAccount(
params: ApiParams,
args: SetCurrentAccountRequestParams
): Promise<boolean> {
const { state, snap } = params;
const { currentAccount } = args;
state.currentAccount = currentAccount;
await updateSnapState(snap, state);
return true;
}
Loading

0 comments on commit d1fc4fe

Please sign in to comment.