Skip to content

Commit

Permalink
feat: implement SDJwt.service
Browse files Browse the repository at this point in the history
  • Loading branch information
SinanovicEdis committed Jan 15, 2025
1 parent 641885a commit 79478a0
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 21 deletions.
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 @@
"@metamask/utils": "9.3.0",
"@sd-jwt/core": "^0.7.2",
"@sd-jwt/crypto-nodejs": "^0.7.2",
"@sd-jwt/sd-jwt-vc": "^0.8.0",
"@sd-jwt/types": "^0.7.2",
"@veramo/core": "6.0.0",
"@veramo/credential-eip712": "6.0.0",
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 @@ -26,7 +26,7 @@
"./files/circuits/credentialAtomicQuerySigV2/circuit_final.zkey",
"./files/circuits/credentialAtomicQuerySigV2/verification_key.json"
],
"shasum": "3JymcCbtQUTtucVLSbE0pEXzzXiF6kvqaYlyxvbc6gc="
"shasum": "nHpRp4CVHapl7TO0A6ImnRO0u3T/y4sPghawmRrfAFo="
},
"initialPermissions": {
"endowment:ethereum-provider": {},
Expand Down
109 changes: 109 additions & 0 deletions packages/snap/src/SDJwt.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { SDJwtInstance } from '@sd-jwt/core';
import WalletService from './Wallet.service';
import { digest, generateSalt } from '@sd-jwt/crypto-nodejs';
import { ec as EC } from 'elliptic';
import crypto from 'node:crypto';

type SdJwtPayload = Record<string, unknown>;

class SDJwtService {
static signer: any;
static verifier: any;
static instance: SDJwtInstance<SdJwtPayload>;

/**
* Helper function to encode in Base64 URL-safe format.
*/
private static toBase64Url(buffer: Buffer): string {
return buffer
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}

/**
* Initializes the SDJwtService.
*
* This method sets up the SDJwtService by retrieving the wallet from the WalletService,
* extracting the private key, and creating the key pair. It also sets up the signer and
* verifier functions for the SDJwtService instance.
*
* @throws {Error} If the wallet cannot be retrieved or keys are missing.
*
* @returns {Promise<void>} A promise that resolves when the initialization is complete.
*/
static async init(): Promise<void> {
try {
const wallet = WalletService.get();

if (!wallet) {
throw new Error('Failed to retrieve keys');
}

const privateKeyHex = wallet.privateKey.slice(2); // Remove '0x' prefix

const ec = new EC('secp256k1');
const keyPair = ec.keyFromPrivate(privateKeyHex, 'hex');
const publicKey = keyPair.getPublic();

if (!keyPair || !publicKey) {
throw new Error('Keys are missing from WalletService');
}

SDJwtService.signer = async (data: string): Promise<string> => {
const hash = crypto.createHash('sha256').update(data).digest();
const signature = keyPair.sign(hash);
return SDJwtService.toBase64Url(
Buffer.concat([
Buffer.from(signature.r.toArray('be', 32)),
Buffer.from(signature.s.toArray('be', 32)),
])
);
};

SDJwtService.verifier = async (
data: string,
signatureBase64Url: string
): Promise<boolean> => {
const hash = crypto.createHash('sha256').update(data).digest();
const signatureBuffer = Buffer.from(signatureBase64Url, 'base64url');
const r = signatureBuffer.subarray(0, 32);
const s = signatureBuffer.subarray(32);
return keyPair.verify(hash, { r, s });
};

SDJwtService.instance = new SDJwtInstance({
signer: SDJwtService.signer
? SDJwtService.signer
: async () => {
throw new Error('Signer not initialized');
},
verifier: SDJwtService.verifier
? SDJwtService.verifier
: async () => {
throw new Error('Verifier not initialized');
},
signAlg: 'EdDSA',
hasher: digest,
hashAlg: 'SHA-256',
saltGenerator: generateSalt,
});
} catch (e) {
console.error('Failed to initialize SDJwtService', e);
}
}

/**
* Get the global SDJwtInstance
* @returns SDJwtInstance
*/
static getInstance(): SDJwtInstance<any> {
if (!SDJwtService.instance) {
throw new Error('---> SDJwtService is not initialized');
}
return SDJwtService.instance;
}
}

export default SDJwtService;
3 changes: 3 additions & 0 deletions packages/snap/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import UIService from './UI.service';
import WalletService from './Wallet.service';
import StorageService from './storage/Storage.service';
import VeramoService from './veramo/Veramo.service';
import SDJwtService from './SDJwt.service';

export const onRpcRequest: OnRpcRequestHandler = async ({
request,
Expand All @@ -33,6 +34,8 @@ export const onRpcRequest: OnRpcRequestHandler = async ({

await WalletService.init();

await SDJwtService.init();

await VeramoService.init();

await EthereumService.init();
Expand Down
29 changes: 9 additions & 20 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 79478a0

Please sign in to comment.