From 06bf5d389cd12e8979d406fef9717d6c6a827254 Mon Sep 17 00:00:00 2001 From: rahulbarmann Date: Thu, 19 Sep 2024 02:33:26 +0530 Subject: [PATCH] feat: add server action for decryption of shares and reconstruct it back to the private key fix: encoding errors in keyShardingService --- package.json | 2 ++ src/actions/pvtKeyDecryptMgmt.ts | 47 ++++++++++++++++++++++++++- src/actions/pvtKeyEncryptMgmt.ts | 13 ++------ src/services/aws-kms-module.ts | 2 -- src/services/keyShardingService.ts | 52 +++++++++++++++++++++--------- src/services/walletService.ts | 3 +- 6 files changed, 89 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 03e9d11..ca9b7f2 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,9 @@ "@solana/wallet-adapter-react-ui": "^0.9.35", "@solana/wallet-adapter-wallets": "^0.19.32", "@solana/web3.js": "^1.95.2", + "@types/bs58": "^4.0.4", "axios": "^1.7.7", + "bs58": "^4.0.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "framer-motion": "^11.3.21", diff --git a/src/actions/pvtKeyDecryptMgmt.ts b/src/actions/pvtKeyDecryptMgmt.ts index 5009459..e67d25d 100644 --- a/src/actions/pvtKeyDecryptMgmt.ts +++ b/src/actions/pvtKeyDecryptMgmt.ts @@ -1 +1,46 @@ -//Here you create decryption + retrieval logic in a top level server action like the other file \ No newline at end of file +'use server' + +import prisma from '@/db' +import { authOptions } from '@/lib/auth' +import { aesDecrypt } from '@/services/aes-module' +import { awsDecrypt } from '@/services/aws-kms-module' +import { gcpDecrypt } from '@/services/gcp-kms-module' +import { combineSecret } from '@/services/keyShardingService' +import { getServerSession } from 'next-auth' +import bs58 from 'bs58' + +export async function pvtKeyDecryptionManager() { + try { + const session = await getServerSession(authOptions) + const userId = session.user.id + + const { aesShare, awsShare, gcpShare }: any = await prisma.user.findFirst({ + where: { id: userId }, + select: { aesShare: true, awsShare: true, gcpShare: true }, + }) + + const decryptedAesShare = aesDecrypt(aesShare) + const decryptedAwsShare = await awsDecrypt(awsShare, { + purpose: 'tiplink', + country: 'India', + }) + const decryptedGcpShare = await gcpDecrypt(gcpShare) + + const aesShareArray = new Uint8Array(Buffer.from(decryptedAesShare, 'hex')) + const awsShareArray = new Uint8Array(Buffer.from(decryptedAwsShare, 'hex')) + const gcpShareArray = new Uint8Array(Buffer.from(decryptedGcpShare, 'hex')) + + const res = await combineSecret([ + aesShareArray, + awsShareArray, + gcpShareArray, + ]) + + const privateKey = bs58.encode(res) + + return privateKey + } catch (error) { + console.error('Error in pvtKeyDecryptionManager:', error) + throw error + } +} diff --git a/src/actions/pvtKeyEncryptMgmt.ts b/src/actions/pvtKeyEncryptMgmt.ts index 3d51a8e..6dc4468 100644 --- a/src/actions/pvtKeyEncryptMgmt.ts +++ b/src/actions/pvtKeyEncryptMgmt.ts @@ -12,25 +12,18 @@ export async function pvtKeyEncryptionManager(privateKey: string) { const session = await getServerSession(authOptions) const userId = session?.user?.id - const { aesShareString, awsShareString, gcpShareString }: any = - await splitSecret(new Uint8Array(Buffer.from(privateKey, 'hex'))) + const { aesShareString, awsShareString, gcpShareString } = + await splitSecret(privateKey) - //AES Share 1 -> share encryption AES module const aesEncryptedShare = aesEncrypt(aesShareString) - //AWS Share 2 -> share encryption AWS module const awsEncryptedShare = await awsEncrypt(awsShareString, { purpose: 'tiplink', country: 'India', }) - - //GCP Share 3 -> share encryption GCP module const gcpEncryptedShare = await gcpEncrypt(gcpShareString) - // DB write await prisma.user.update({ - where: { - id: userId, - }, + where: { id: userId }, data: { aesShare: aesEncryptedShare, awsShare: awsEncryptedShare, diff --git a/src/services/aws-kms-module.ts b/src/services/aws-kms-module.ts index cf03514..f20477c 100644 --- a/src/services/aws-kms-module.ts +++ b/src/services/aws-kms-module.ts @@ -39,8 +39,6 @@ export async function awsDecrypt( try { const encryptedBuffer = Buffer.from(encryptedData, 'base64') const { plaintext, messageHeader } = await decrypt(keyring, encryptedBuffer) - console.log('===== Message Header =======') - console.log(JSON.stringify(messageHeader.encryptionContext)) Object.entries(context).forEach(([key, value]) => { if (messageHeader.encryptionContext[key] !== value) { diff --git a/src/services/keyShardingService.ts b/src/services/keyShardingService.ts index a6460ef..729f8a8 100644 --- a/src/services/keyShardingService.ts +++ b/src/services/keyShardingService.ts @@ -1,21 +1,41 @@ -import { split as shamirSplit, combine as shamirCombine } from 'shamir-secret-sharing'; -const CryptoJS = require('crypto-js'); +import { + split as shamirSplit, + combine as shamirCombine, +} from 'shamir-secret-sharing' -export async function splitSecret(secretKey: Uint8Array) { - if (!secretKey) { - throw new Error('Secret is undefined'); - } - try { - const shares = await shamirSplit(secretKey, 3, 3); +import * as bs58 from 'bs58' - const [aesShare, awsShare, gcpShare] = shares; - const aesShareString = Buffer.from(aesShare).toString('hex'); - const awsShareString = Buffer.from(awsShare).toString('hex'); - const gcpShareString = Buffer.from(gcpShare).toString('hex'); +export async function splitSecret(privateKey: string) { + if (!privateKey) { + throw new Error('Private key is undefined') + } + try { + const secretKeyUint8Array = new Uint8Array(bs58.decode(privateKey)) + const shares = await shamirSplit(secretKeyUint8Array, 3, 3) - return { aesShareString, awsShareString, gcpShareString }; - } catch (error) { - console.error('Error splitting and encrypting secret:', error); - } + const [aesShare, awsShare, gcpShare] = shares + const aesShareString = Buffer.from(aesShare).toString('hex') + const awsShareString = Buffer.from(awsShare).toString('hex') + const gcpShareString = Buffer.from(gcpShare).toString('hex') + + return { aesShareString, awsShareString, gcpShareString } + } catch (error) { + console.error('Error splitting secret:', error) + throw error + } +} + +export async function combineSecret(shares: Uint8Array[]) { + if (!shares || shares.length === 0) { + throw new Error('Shares are undefined or empty') + } + + try { + const secretKey = await shamirCombine(shares) + return new Uint8Array(secretKey) + } catch (e) { + console.error('Error while combining shares: ', e) + throw e + } } diff --git a/src/services/walletService.ts b/src/services/walletService.ts index 2d58da9..d77e2a8 100644 --- a/src/services/walletService.ts +++ b/src/services/walletService.ts @@ -4,6 +4,7 @@ import prisma from '@/db' import { User } from '@prisma/client' import axios from 'axios' import { pvtKeyEncryptionManager } from '@/actions/pvtKeyEncryptMgmt' +import base58 from 'bs58' const customRpcUrl = process.env.NEXT_PUBLIC_SOLANA_RPC || '' @@ -20,7 +21,7 @@ export async function createWallet(user: User) { try { const keypair = Keypair.generate() const publicKey = keypair.publicKey.toString() - const privateKey = keypair.secretKey.toString() + const privateKey = base58.encode(keypair.secretKey) await pvtKeyEncryptionManager(privateKey) await prisma.user.update({ where: {