diff --git a/package-lock.json b/package-lock.json index 1dba0ca..87ba6b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@hookform/resolvers": "^3.9.0", "@noble/ed25519": "^2.2.3", "@noble/hashes": "^1.8.0", + "@noble/secp256k1": "^2.3.0", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-aspect-ratio": "^1.1.0", @@ -64,6 +65,7 @@ "tailwindcss-animate": "^1.0.7", "uuid": "^11.1.0", "vaul": "^0.9.3", + "viem": "^2.34.0", "wagmi": "^2.16.1", "zod": "^3.23.8" }, @@ -2129,16 +2131,13 @@ } }, "node_modules/@noble/secp256k1": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.2.tgz", - "integrity": "sha512-/qzwYl5eFLH8OWIecQWM31qld2g1NfjgylK+TNhqtaUKP37Nm+Y+z30Fjhw0Ct8p9yCQEm2N3W/AckdIb3SMcQ==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-2.3.0.tgz", + "integrity": "sha512-0TQed2gcBbIrh7Ccyw+y/uZQvbJwm7Ao4scBUxqpBCcsOlZG0O4KGfjtNAy/li4W8n1xt3dxrwJ0beZ2h2G6Kw==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -5564,6 +5563,18 @@ } } }, + "node_modules/@waku/enr/node_modules/@noble/secp256k1": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.2.tgz", + "integrity": "sha512-/qzwYl5eFLH8OWIecQWM31qld2g1NfjgylK+TNhqtaUKP37Nm+Y+z30Fjhw0Ct8p9yCQEm2N3W/AckdIb3SMcQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, "node_modules/@waku/interfaces": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@waku/interfaces/-/interfaces-0.0.29.tgz", @@ -12069,6 +12080,18 @@ "base64-js": "^1.5.1" } }, + "node_modules/jsontokens/node_modules/@noble/secp256k1": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.2.tgz", + "integrity": "sha512-/qzwYl5eFLH8OWIecQWM31qld2g1NfjgylK+TNhqtaUKP37Nm+Y+z30Fjhw0Ct8p9yCQEm2N3W/AckdIb3SMcQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, "node_modules/keccak": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", @@ -15628,9 +15651,9 @@ } }, "node_modules/viem": { - "version": "2.33.2", - "resolved": "https://registry.npmjs.org/viem/-/viem-2.33.2.tgz", - "integrity": "sha512-/720OaM4dHWs8vXwNpyet+PRERhPaW+n/1UVSCzyb9jkmwwVfaiy/R6YfCFb4v+XXbo8s3Fapa3DM5yCRSkulA==", + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.34.0.tgz", + "integrity": "sha512-HJZG9Wt0DLX042MG0PK17tpataxtdAEhpta9/Q44FqKwy3xZMI5Lx4jF+zZPuXFuYjZ68R0PXqRwlswHs6r4gA==", "funding": [ { "type": "github", @@ -15639,14 +15662,14 @@ ], "license": "MIT", "dependencies": { - "@noble/curves": "1.9.2", + "@noble/curves": "1.9.6", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.0.8", "isows": "1.0.7", - "ox": "0.8.6", - "ws": "8.18.2" + "ox": "0.8.7", + "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" @@ -15669,6 +15692,21 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/viem/node_modules/@noble/curves": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.6.tgz", + "integrity": "sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/viem/node_modules/@scure/bip32": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", @@ -15703,9 +15741,9 @@ "license": "MIT" }, "node_modules/viem/node_modules/ox": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/ox/-/ox-0.8.6.tgz", - "integrity": "sha512-eiKcgiVVEGDtEpEdFi1EGoVVI48j6icXHce9nFwCNM7CKG3uoCXKdr4TPhS00Iy1TR2aWSF1ltPD0x/YgqIL9w==", + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.8.7.tgz", + "integrity": "sha512-W1f0FiMf9NZqtHPEDEAEkyzZDwbIKfmH2qmQx8NNiQ/9JhxrSblmtLJsSfTtQG5YKowLOnBlLVguCyxm/7ztxw==", "funding": [ { "type": "github", @@ -15732,27 +15770,6 @@ } } }, - "node_modules/viem/node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/vite": { "version": "5.4.10", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", @@ -16327,9 +16344,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", "engines": { "node": ">=10.0.0" diff --git a/package.json b/package.json index 484ae14..3130040 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@hookform/resolvers": "^3.9.0", "@noble/ed25519": "^2.2.3", "@noble/hashes": "^1.8.0", + "@noble/secp256k1": "^2.3.0", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-aspect-ratio": "^1.1.0", @@ -69,6 +70,7 @@ "tailwindcss-animate": "^1.0.7", "uuid": "^11.1.0", "vaul": "^0.9.3", + "viem": "^2.34.0", "wagmi": "^2.16.1", "zod": "^3.23.8" }, diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index 045fe5e..02c80e7 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -21,7 +21,7 @@ interface AuthContextType { delegationTimeRemaining: () => number; clearDelegation: () => void; signMessage: (message: OpchanMessage) => Promise; - verifyMessage: (message: OpchanMessage) => boolean; + verifyMessage: (message: OpchanMessage) => Promise; } const AuthContext = createContext(undefined); @@ -305,7 +305,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { signMessage: async (message: OpchanMessage): Promise => { return authServiceRef.current.signMessage(message); }, - verifyMessage: (message: OpchanMessage): boolean => { + verifyMessage: async (message: OpchanMessage): Promise => { return authServiceRef.current.verifyMessage(message); } }; diff --git a/src/lib/identity/services/AuthService.ts b/src/lib/identity/services/AuthService.ts index 71bbf42..812f50b 100644 --- a/src/lib/identity/services/AuthService.ts +++ b/src/lib/identity/services/AuthService.ts @@ -290,7 +290,7 @@ export class AuthService { /** * Verify a message signature */ - verifyMessage(message: OpchanMessage): boolean { + async verifyMessage(message: OpchanMessage): Promise { return this.messageSigning.verifyMessage(message); } diff --git a/src/lib/identity/services/MessageService.ts b/src/lib/identity/services/MessageService.ts index 1f0e956..b3bdba4 100644 --- a/src/lib/identity/services/MessageService.ts +++ b/src/lib/identity/services/MessageService.ts @@ -65,7 +65,7 @@ export class MessageService { /** * Verify a message signature */ - verifyMessage(message: OpchanMessage): boolean { + async verifyMessage(message: OpchanMessage): Promise { return this.authService.verifyMessage(message); } } \ No newline at end of file diff --git a/src/lib/identity/signatures/key-delegation.ts b/src/lib/identity/signatures/key-delegation.ts index 0ec916c..708e683 100644 --- a/src/lib/identity/signatures/key-delegation.ts +++ b/src/lib/identity/signatures/key-delegation.ts @@ -79,6 +79,7 @@ export class KeyDelegation { * @param browserPrivateKey The browser-generated private key * @param duration The duration of the delegation ('1week' or '30days') * @param walletType The type of wallet (bitcoin or ethereum) + * @param walletPublicKey The public key of the wallet (for signature verification) * @returns DelegationInfo object */ createDelegation( @@ -87,7 +88,8 @@ export class KeyDelegation { browserPublicKey: string, browserPrivateKey: string, duration: DelegationDuration = '7days', - walletType: 'bitcoin' | 'ethereum' + walletType: 'bitcoin' | 'ethereum', + walletPublicKey?: string ): DelegationInfo { const expiryHours = KeyDelegation.getDurationHours(duration); const expiryTimestamp = Date.now() + (expiryHours * 60 * 60 * 1000); @@ -98,7 +100,8 @@ export class KeyDelegation { browserPublicKey, browserPrivateKey, walletAddress, - walletType + walletType, + walletPublicKey }; } diff --git a/src/lib/identity/signatures/message-signing.test.ts b/src/lib/identity/signatures/message-signing.test.ts new file mode 100644 index 0000000..04775e2 --- /dev/null +++ b/src/lib/identity/signatures/message-signing.test.ts @@ -0,0 +1,241 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { MessageSigning } from './message-signing'; +import { KeyDelegation } from './key-delegation'; +import { MessageType, PostMessage } from '@/lib/waku/types'; + +// Mock the KeyDelegation class +vi.mock('./key-delegation'); + +// Mock the WalletSignatureVerifier +vi.mock('./wallet-signature-verifier', () => ({ + WalletSignatureVerifier: { + verifyWalletSignature: vi.fn().mockReturnValue(true) + } +})); + +describe('MessageSigning with Delegation Chain Verification', () => { + let messageSigning: MessageSigning; + let mockKeyDelegation: KeyDelegation; + + beforeEach(() => { + // Create a mock delegation + const mockDelegation = { + signature: 'mock-wallet-signature', + expiryTimestamp: Date.now() + 24 * 60 * 60 * 1000, // 24 hours from now + browserPublicKey: 'mock-browser-public-key', + browserPrivateKey: 'mock-browser-private-key', + walletAddress: 'mock-wallet-address', + walletType: 'bitcoin' as const, + walletPublicKey: 'mock-wallet-public-key' // Add wallet public key for verification + }; + + // Setup mock methods + mockKeyDelegation = { + isDelegationValid: vi.fn().mockReturnValue(true), + retrieveDelegation: vi.fn().mockReturnValue(mockDelegation), + signMessage: vi.fn().mockReturnValue('mock-message-signature'), + verifySignature: vi.fn().mockReturnValue(true), + createDelegationMessage: vi.fn().mockReturnValue('I, mock-wallet-address, delegate authority to this pubkey: mock-browser-public-key until 1234567890'), + getDelegationTimeRemaining: vi.fn().mockReturnValue(24 * 60 * 60 * 1000), + clearDelegation: vi.fn(), + getBrowserPublicKey: vi.fn().mockReturnValue('mock-browser-public-key'), + getDelegatingAddress: vi.fn().mockReturnValue('mock-wallet-address'), + generateKeypair: vi.fn().mockReturnValue({ + publicKey: 'mock-browser-public-key', + privateKey: 'mock-browser-private-key' + }), + createDelegation: vi.fn().mockReturnValue(mockDelegation), + storeDelegation: vi.fn() + }; + + // Mock the KeyDelegation constructor + vi.mocked(KeyDelegation).mockImplementation(() => mockKeyDelegation); + + messageSigning = new MessageSigning(mockKeyDelegation); + }); + + describe('signMessage', () => { + it('should sign a message with delegation chain information', () => { + const message: PostMessage = { + type: MessageType.POST, + timestamp: Date.now(), + author: 'mock-wallet-address', + id: 'test-post-1', + cellId: 'test-cell-1', + title: 'Test Post', + content: 'Test content' + }; + + const signedMessage = messageSigning.signMessage(message); + + expect(signedMessage).not.toBeNull(); + expect(signedMessage).toHaveProperty('signature', 'mock-message-signature'); + expect(signedMessage).toHaveProperty('browserPubKey', 'mock-browser-public-key'); + expect(signedMessage).toHaveProperty('delegationSignature', 'mock-wallet-signature'); + expect(signedMessage).toHaveProperty('delegationMessage'); + expect(signedMessage).toHaveProperty('delegationExpiry'); + }); + + it('should return null when delegation is invalid', () => { + mockKeyDelegation.isDelegationValid = vi.fn().mockReturnValue(false); + + const message: PostMessage = { + type: MessageType.POST, + timestamp: Date.now(), + author: 'mock-wallet-address', + id: 'test-post-1', + cellId: 'test-cell-1', + title: 'Test Post', + content: 'Test content' + }; + + const signedMessage = messageSigning.signMessage(message); + expect(signedMessage).toBeNull(); + }); + }); + + describe('verifyMessage', () => { + it('should verify a valid message with delegation chain', async () => { + const message: PostMessage & { + signature: string; + browserPubKey: string; + delegationSignature: string; + delegationMessage: string; + delegationExpiry: number; + } = { + type: MessageType.POST, + timestamp: Date.now(), + author: 'mock-wallet-address', + id: 'test-post-1', + cellId: 'test-cell-1', + title: 'Test Post', + content: 'Test content', + signature: 'mock-message-signature', + browserPubKey: 'mock-browser-public-key', + delegationSignature: 'mock-wallet-signature', + delegationMessage: 'I, mock-wallet-address, delegate authority to this pubkey: mock-browser-public-key until 1234567890', + delegationExpiry: Date.now() + 24 * 60 * 60 * 1000 + }; + + const isValid = await messageSigning.verifyMessage(message); + expect(isValid).toBe(true); + }); + + it('should reject message with missing signature fields', async () => { + const message: PostMessage = { + type: MessageType.POST, + timestamp: Date.now(), + author: 'mock-wallet-address', + id: 'test-post-1', + cellId: 'test-cell-1', + title: 'Test Post', + content: 'Test content' + // Missing signature fields + }; + + const isValid = await messageSigning.verifyMessage(message); + expect(isValid).toBe(false); + }); + + it('should reject message with missing delegation fields', async () => { + const message: PostMessage & { + signature: string; + browserPubKey: string; + } = { + type: MessageType.POST, + timestamp: Date.now(), + author: 'mock-wallet-address', + id: 'test-post-1', + cellId: 'test-cell-1', + title: 'Test Post', + content: 'Test content', + signature: 'mock-message-signature', + browserPubKey: 'mock-browser-public-key' + // Missing delegation fields + }; + + const isValid = await messageSigning.verifyMessage(message); + expect(isValid).toBe(false); + }); + + it('should reject message with invalid signature', async () => { + mockKeyDelegation.verifySignature = vi.fn().mockReturnValue(false); + + const message: PostMessage & { + signature: string; + browserPubKey: string; + delegationSignature: string; + delegationMessage: string; + delegationExpiry: number; + } = { + type: MessageType.POST, + timestamp: Date.now(), + author: 'mock-wallet-address', + id: 'test-post-1', + cellId: 'test-cell-1', + title: 'Test Post', + content: 'Test content', + signature: 'invalid-signature', + browserPubKey: 'mock-browser-public-key', + delegationSignature: 'mock-wallet-signature', + delegationMessage: 'I, mock-wallet-address, delegate authority to this pubkey: mock-browser-public-key until 1234567890', + delegationExpiry: Date.now() + 24 * 60 * 60 * 1000 + }; + + const isValid = await messageSigning.verifyMessage(message); + expect(isValid).toBe(false); + }); + + it('should reject message with expired delegation', async () => { + const message: PostMessage & { + signature: string; + browserPubKey: string; + delegationSignature: string; + delegationMessage: string; + delegationExpiry: number; + } = { + type: MessageType.POST, + timestamp: Date.now(), + author: 'mock-wallet-address', + id: 'test-post-1', + cellId: 'test-cell-1', + title: 'Test Post', + content: 'Test content', + signature: 'mock-message-signature', + browserPubKey: 'mock-browser-public-key', + delegationSignature: 'mock-wallet-signature', + delegationMessage: 'I, mock-wallet-address, delegate authority to this pubkey: mock-browser-public-key until 1234567890', + delegationExpiry: Date.now() - 24 * 60 * 60 * 1000 // Expired 24 hours ago + }; + + const isValid = await messageSigning.verifyMessage(message); + expect(isValid).toBe(false); + }); + + it('should reject message with tampered delegation message', async () => { + const message: PostMessage & { + signature: string; + browserPubKey: string; + delegationSignature: string; + delegationMessage: string; + delegationExpiry: number; + } = { + type: MessageType.POST, + timestamp: Date.now(), + author: 'mock-wallet-address', + id: 'test-post-1', + cellId: 'test-cell-1', + title: 'Test Post', + content: 'Test content', + signature: 'mock-message-signature', + browserPubKey: 'mock-browser-public-key', + delegationSignature: 'mock-wallet-signature', + delegationMessage: 'I, attacker-address, delegate authority to this pubkey: mock-browser-public-key until 1234567890', // Tampered + delegationExpiry: Date.now() + 24 * 60 * 60 * 1000 + }; + + const isValid = await messageSigning.verifyMessage(message); + expect(isValid).toBe(false); + }); + }); +}); diff --git a/src/lib/identity/signatures/message-signing.ts b/src/lib/identity/signatures/message-signing.ts index 8c59ff9..25e74e5 100644 --- a/src/lib/identity/signatures/message-signing.ts +++ b/src/lib/identity/signatures/message-signing.ts @@ -1,7 +1,6 @@ import { OpchanMessage } from '@/types'; import { KeyDelegation } from './key-delegation'; - - +import { WalletSignatureVerifier } from './wallet-signature-verifier'; export class MessageSigning { private keyDelegation: KeyDelegation; @@ -22,7 +21,10 @@ export class MessageSigning { const messageToSign = JSON.stringify({ ...message, signature: undefined, - browserPubKey: undefined + browserPubKey: undefined, + delegationSignature: undefined, + delegationMessage: undefined, + delegationExpiry: undefined }); const signature = this.keyDelegation.signMessage(messageToSign); @@ -31,11 +33,24 @@ export class MessageSigning { return { ...message, signature, - browserPubKey: delegation.browserPublicKey + browserPubKey: delegation.browserPublicKey, + delegationSignature: delegation.signature, + delegationMessage: this.keyDelegation.createDelegationMessage( + delegation.browserPublicKey, + delegation.walletAddress, + delegation.expiryTimestamp + ), + delegationExpiry: delegation.expiryTimestamp }; } - verifyMessage(message: OpchanMessage): boolean { + async verifyMessage(message: OpchanMessage & { + signature?: string; + browserPubKey?: string; + delegationSignature?: string; + delegationMessage?: string; + delegationExpiry?: number; + }): Promise { // Check for required signature fields if (!message.signature || !message.browserPubKey) { const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`; @@ -43,29 +58,95 @@ export class MessageSigning { return false; } - + // Check for required delegation fields + if (!message.delegationSignature || !message.delegationMessage || !message.delegationExpiry) { + const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`; + console.warn('Message is missing delegation information', messageId); + return false; + } - // Reconstruct the original signed content + // 1. Verify the message signature const signedContent = JSON.stringify({ ...message, signature: undefined, - browserPubKey: undefined + browserPubKey: undefined, + delegationSignature: undefined, + delegationMessage: undefined, + delegationExpiry: undefined }); - // Verify the signature - const isValid = this.keyDelegation.verifySignature( + const isValidMessageSignature = this.keyDelegation.verifySignature( signedContent, message.signature, message.browserPubKey ); - if (!isValid) { + if (!isValidMessageSignature) { const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`; - console.warn(`Invalid signature for message ${messageId}`); + console.warn(`Invalid message signature for message ${messageId}`); + return false; } - return isValid; + // 2. Verify delegation hasn't expired + const now = Date.now(); + if (now >= message.delegationExpiry) { + const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`; + console.warn(`Delegation expired for message ${messageId}`); + return false; + } + + // 3. Verify delegation message integrity + const expectedDelegationMessage = this.keyDelegation.createDelegationMessage( + message.browserPubKey, + message.author, + message.delegationExpiry + ); + + if (message.delegationMessage !== expectedDelegationMessage) { + const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`; + console.warn(`Delegation message tampered for message ${messageId}`); + return false; + } + + // 4. Verify wallet signature of delegation + const isValidDelegationSignature = await this.verifyWalletSignature( + message.delegationMessage, + message.delegationSignature, + message.author + ); + + if (!isValidDelegationSignature) { + const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`; + console.warn(`Invalid delegation signature for message ${messageId}`); + return false; + } + + return true; } - + /** + * Verify wallet signature of delegation message + * Uses proper cryptographic verification based on wallet type + */ + private async verifyWalletSignature( + delegationMessage: string, + signature: string, + walletAddress: string + ): Promise { + // Get the wallet type from the delegation + const delegation = this.keyDelegation.retrieveDelegation(); + if (!delegation) { + console.warn('No delegation found for wallet signature verification'); + return false; + } + + // Use the proper wallet signature verifier with public key + return await WalletSignatureVerifier.verifyWalletSignature( + delegationMessage, + signature, + walletAddress, + delegation.walletType, + delegation.walletPublicKey + ); + } } \ No newline at end of file diff --git a/src/lib/identity/signatures/types.ts b/src/lib/identity/signatures/types.ts index 62fa08a..e5ff1e6 100644 --- a/src/lib/identity/signatures/types.ts +++ b/src/lib/identity/signatures/types.ts @@ -4,6 +4,7 @@ export interface DelegationSignature { browserPublicKey: string; // Browser-generated public key that was delegated to walletAddress: string; // Wallet address that signed the delegation walletType: 'bitcoin' | 'ethereum'; // Type of wallet that created the delegation + walletPublicKey?: string; // Public key of the wallet (for signature verification) } export interface DelegationInfo extends DelegationSignature { diff --git a/src/lib/waku/types.ts b/src/lib/waku/types.ts index 026627a..30f4acb 100644 --- a/src/lib/waku/types.ts +++ b/src/lib/waku/types.ts @@ -10,14 +10,20 @@ export enum MessageType { } /** - * Base interface for all message types + * Base interface for all message types with delegation chain security */ export interface BaseMessage { type: MessageType; timestamp: number; author: string; + + // Message signature verification fields signature?: string; // Message signature for verification browserPubKey?: string; // Public key that signed the message + + delegationSignature?: string; // Original wallet signature of delegation + delegationMessage?: string; // Original delegation message that was signed + delegationExpiry?: number; // When the delegation expires } /** diff --git a/src/types/index.ts b/src/types/index.ts index ceb76dc..3f21599 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -74,9 +74,3 @@ export interface Comment { relevanceScore?: number; // Calculated relevance score relevanceDetails?: RelevanceScoreDetails; // Detailed breakdown of relevance score calculation } - -// Extended message types for verification -export interface SignedMessage { - signature?: string; // Signature of the message - browserPubKey?: string; // Public key that signed the message -}