feat: Implement Sign Request Manager component for handling sign requests in the popup (WIP)
- Added SignRequestManager.js to manage sign requests, including UI states for keyspace lock, mismatch, and approval. - Implemented methods for loading state, rendering UI, and handling user interactions (approve/reject requests). - Integrated background message listeners for keyspace unlock events and request updates.
This commit is contained in:
parent
a0622629ae
commit
580fd72dce
@ -5,6 +5,7 @@ let keepAliveInterval = null;
|
||||
let sessionTimeoutDuration = 15; // Default 15 seconds
|
||||
let sessionTimeoutId = null; // Background timer
|
||||
let popupPort = null; // Track popup connection
|
||||
let sigSocketService = null; // SigSocket service instance
|
||||
|
||||
// Utility function to convert Uint8Array to hex
|
||||
function toHex(uint8Array) {
|
||||
@ -138,6 +139,9 @@ async function restoreSession() {
|
||||
// Import WASM module functions
|
||||
import init, * as wasmFunctions from './wasm/wasm_app.js';
|
||||
|
||||
// Import SigSocket service
|
||||
import SigSocketService from './background/sigsocket.js';
|
||||
|
||||
// Initialize WASM module
|
||||
async function initVault() {
|
||||
try {
|
||||
@ -151,6 +155,9 @@ async function initVault() {
|
||||
vault = wasmFunctions;
|
||||
isInitialized = true;
|
||||
|
||||
// Initialize SigSocket service
|
||||
await initSigSocketService();
|
||||
|
||||
// Try to restore previous session
|
||||
await restoreSession();
|
||||
|
||||
@ -161,6 +168,17 @@ async function initVault() {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize SigSocket service
|
||||
async function initSigSocketService() {
|
||||
try {
|
||||
sigSocketService = new SigSocketService();
|
||||
await sigSocketService.initialize(vault);
|
||||
console.log('SigSocket service initialized');
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize SigSocket service:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Consolidated message handlers
|
||||
const messageHandlers = {
|
||||
@ -172,6 +190,16 @@ const messageHandlers = {
|
||||
initSession: async (request) => {
|
||||
await vault.init_session(request.keyspace, request.password);
|
||||
await sessionManager.save(request.keyspace);
|
||||
|
||||
// Auto-connect to SigSocket server when session is initialized (only if not already connected)
|
||||
if (sigSocketService && !sigSocketService.isConnected) {
|
||||
console.log(`Attempting to connect to SigSocket for keyspace: ${request.keyspace}`);
|
||||
const connected = await sigSocketService.connectToServer(request.keyspace);
|
||||
console.log(`SigSocket connection result: ${connected}`);
|
||||
} else if (sigSocketService && sigSocketService.isConnected) {
|
||||
console.log('SigSocket already connected, skipping connection attempt');
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
|
||||
@ -261,6 +289,52 @@ const messageHandlers = {
|
||||
await chrome.storage.local.set({ sessionTimeout: request.timeout });
|
||||
resetSessionTimeout(); // Restart with new duration
|
||||
return { success: true };
|
||||
},
|
||||
|
||||
// SigSocket message handlers
|
||||
connectSigSocket: async (request) => {
|
||||
if (!sigSocketService) {
|
||||
return { success: false, error: 'SigSocket service not available' };
|
||||
}
|
||||
const connected = await sigSocketService.connectToServer(request.workspaceId);
|
||||
return { success: connected };
|
||||
},
|
||||
|
||||
getPendingRequests: () => {
|
||||
if (!sigSocketService) {
|
||||
return { success: false, error: 'SigSocket service not available' };
|
||||
}
|
||||
return { success: true, requests: sigSocketService.getPendingRequests() };
|
||||
},
|
||||
|
||||
keypaceUnlocked: async () => {
|
||||
if (sigSocketService) {
|
||||
await sigSocketService.onKeypaceUnlocked();
|
||||
}
|
||||
return { success: true };
|
||||
},
|
||||
|
||||
approveSignRequest: async (request) => {
|
||||
if (!sigSocketService) {
|
||||
return { success: false, error: 'SigSocket service not available' };
|
||||
}
|
||||
const approved = await sigSocketService.approveSignRequest(request.requestId);
|
||||
return { success: approved };
|
||||
},
|
||||
|
||||
rejectSignRequest: async (request) => {
|
||||
if (!sigSocketService) {
|
||||
return { success: false, error: 'SigSocket service not available' };
|
||||
}
|
||||
const rejected = await sigSocketService.rejectSignRequest(request.requestId, request.reason);
|
||||
return { success: rejected };
|
||||
},
|
||||
|
||||
getSigSocketStatus: () => {
|
||||
if (!sigSocketService) {
|
||||
return { success: false, error: 'SigSocket service not available' };
|
||||
}
|
||||
return { success: true, status: sigSocketService.getStatus() };
|
||||
}
|
||||
};
|
||||
|
||||
@ -302,6 +376,11 @@ chrome.runtime.onConnect.addListener((port) => {
|
||||
// Track popup connection
|
||||
popupPort = port;
|
||||
|
||||
// Set popup port in SigSocket service
|
||||
if (sigSocketService) {
|
||||
sigSocketService.setPopupPort(port);
|
||||
}
|
||||
|
||||
// If we have an active session, ensure keep-alive is running
|
||||
if (currentSession) {
|
||||
startKeepAlive();
|
||||
@ -310,6 +389,9 @@ chrome.runtime.onConnect.addListener((port) => {
|
||||
port.onDisconnect.addListener(() => {
|
||||
// Popup closed, clear reference and stop keep-alive
|
||||
popupPort = null;
|
||||
if (sigSocketService) {
|
||||
sigSocketService.setPopupPort(null);
|
||||
}
|
||||
stopKeepAlive();
|
||||
});
|
||||
}
|
||||
|
476
crypto_vault_extension/background/sigsocket.js
Normal file
476
crypto_vault_extension/background/sigsocket.js
Normal file
@ -0,0 +1,476 @@
|
||||
/**
|
||||
* SigSocket Service for Browser Extension
|
||||
*
|
||||
* Handles SigSocket client functionality including:
|
||||
* - Auto-connecting to SigSocket server when workspace is created
|
||||
* - Managing pending sign requests
|
||||
* - Handling user approval/rejection flow
|
||||
* - Validating keyspace matches before showing approval UI
|
||||
*/
|
||||
|
||||
class SigSocketService {
|
||||
constructor() {
|
||||
this.connection = null;
|
||||
this.pendingRequests = new Map(); // requestId -> SignRequestData
|
||||
this.connectedPublicKey = null;
|
||||
this.isConnected = false;
|
||||
this.defaultServerUrl = "ws://localhost:8080/ws";
|
||||
|
||||
// Initialize WASM module reference
|
||||
this.wasmModule = null;
|
||||
|
||||
// Reference to popup port for communication
|
||||
this.popupPort = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the service with WASM module
|
||||
* @param {Object} wasmModule - The loaded WASM module
|
||||
*/
|
||||
async initialize(wasmModule) {
|
||||
this.wasmModule = wasmModule;
|
||||
|
||||
// Load server URL from storage
|
||||
try {
|
||||
const result = await chrome.storage.local.get(['sigSocketUrl']);
|
||||
if (result.sigSocketUrl) {
|
||||
this.defaultServerUrl = result.sigSocketUrl;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to load SigSocket URL from storage:', error);
|
||||
}
|
||||
|
||||
// Set up global callbacks for WASM
|
||||
globalThis.onSignRequestReceived = this.handleIncomingRequest.bind(this);
|
||||
globalThis.onConnectionStateChanged = this.handleConnectionStateChange.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to SigSocket server for a workspace
|
||||
* @param {string} workspaceId - The workspace/keyspace identifier
|
||||
* @returns {Promise<boolean>} - True if connected successfully
|
||||
*/
|
||||
async connectToServer(workspaceId) {
|
||||
try {
|
||||
if (!this.wasmModule) {
|
||||
throw new Error('WASM module not initialized');
|
||||
}
|
||||
|
||||
// Check if already connected to this workspace
|
||||
if (this.isConnected && this.connection) {
|
||||
console.log(`Already connected to SigSocket server for workspace: ${workspaceId}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disconnect any existing connection first
|
||||
if (this.connection) {
|
||||
this.disconnect();
|
||||
}
|
||||
|
||||
// Get the workspace default public key
|
||||
const publicKeyHex = await this.wasmModule.get_workspace_default_public_key(workspaceId);
|
||||
if (!publicKeyHex) {
|
||||
throw new Error('No public key found for workspace');
|
||||
}
|
||||
|
||||
console.log(`Connecting to SigSocket server for workspace: ${workspaceId} with key: ${publicKeyHex.substring(0, 16)}...`);
|
||||
|
||||
// Create new SigSocket connection
|
||||
console.log('Creating new SigSocketConnection instance');
|
||||
this.connection = new this.wasmModule.SigSocketConnection();
|
||||
console.log('SigSocketConnection instance created');
|
||||
|
||||
// Connect to server
|
||||
await this.connection.connect(this.defaultServerUrl, publicKeyHex);
|
||||
|
||||
this.connectedPublicKey = publicKeyHex;
|
||||
|
||||
// Clear pending requests if switching to a different workspace
|
||||
if (this.currentWorkspace && this.currentWorkspace !== workspaceId) {
|
||||
console.log(`Switching workspace from ${this.currentWorkspace} to ${workspaceId}, clearing pending requests`);
|
||||
this.pendingRequests.clear();
|
||||
this.updateBadge();
|
||||
}
|
||||
|
||||
this.currentWorkspace = workspaceId;
|
||||
this.isConnected = true;
|
||||
|
||||
console.log(`Successfully connected to SigSocket server for workspace: ${workspaceId}`);
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to connect to SigSocket server:', error);
|
||||
this.isConnected = false;
|
||||
this.connectedPublicKey = null;
|
||||
this.currentWorkspace = null;
|
||||
if (this.connection) {
|
||||
this.connection.disconnect();
|
||||
this.connection = null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle incoming sign request from server
|
||||
* @param {string} requestId - Unique request identifier
|
||||
* @param {string} messageBase64 - Message to be signed (base64-encoded)
|
||||
*/
|
||||
handleIncomingRequest(requestId, messageBase64) {
|
||||
console.log(`Received sign request: ${requestId}`);
|
||||
|
||||
// Security check: Only accept requests if we have an active connection
|
||||
if (!this.isConnected || !this.connectedPublicKey || !this.currentWorkspace) {
|
||||
console.warn(`Rejecting sign request ${requestId}: No active workspace connection`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the request with workspace info
|
||||
const requestData = {
|
||||
id: requestId,
|
||||
message: messageBase64,
|
||||
timestamp: Date.now(),
|
||||
status: 'pending',
|
||||
workspace: this.currentWorkspace,
|
||||
connectedPublicKey: this.connectedPublicKey
|
||||
};
|
||||
|
||||
this.pendingRequests.set(requestId, requestData);
|
||||
|
||||
console.log(`Stored sign request for workspace: ${this.currentWorkspace}`);
|
||||
|
||||
// Show notification to user
|
||||
this.showSignRequestNotification();
|
||||
|
||||
// Update extension badge
|
||||
this.updateBadge();
|
||||
|
||||
// Notify popup about new request if it's open and keyspace is unlocked
|
||||
this.notifyPopupOfNewRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle connection state changes
|
||||
* @param {boolean} connected - True if connected, false if disconnected
|
||||
*/
|
||||
handleConnectionStateChange(connected) {
|
||||
this.isConnected = connected;
|
||||
console.log(`SigSocket connection state changed: ${connected ? 'connected' : 'disconnected'}`);
|
||||
|
||||
if (!connected) {
|
||||
this.connectedPublicKey = null;
|
||||
this.currentWorkspace = null;
|
||||
// Optionally attempt reconnection here
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when keyspace is unlocked - validate and show/hide approval UI
|
||||
*/
|
||||
async onKeypaceUnlocked() {
|
||||
try {
|
||||
if (!this.wasmModule) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only check keyspace match if we have a connection
|
||||
if (!this.isConnected || !this.connectedPublicKey) {
|
||||
console.log('No SigSocket connection to validate against');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the currently unlocked workspace name
|
||||
const unlockedWorkspaceName = this.wasmModule.get_current_keyspace_name();
|
||||
|
||||
// Get workspace default public key for the UNLOCKED workspace (not connected workspace)
|
||||
const unlockedWorkspacePublicKey = await this.wasmModule.get_workspace_default_public_key(unlockedWorkspaceName);
|
||||
|
||||
// Check if the unlocked workspace matches the connected workspace
|
||||
const workspaceMatches = unlockedWorkspaceName === this.currentWorkspace;
|
||||
const publicKeyMatches = unlockedWorkspacePublicKey === this.connectedPublicKey;
|
||||
const keypaceMatches = workspaceMatches && publicKeyMatches;
|
||||
|
||||
console.log(`Keyspace unlock validation:`);
|
||||
console.log(` Connected workspace: ${this.currentWorkspace}`);
|
||||
console.log(` Unlocked workspace: ${unlockedWorkspaceName}`);
|
||||
console.log(` Connected public key: ${this.connectedPublicKey}`);
|
||||
console.log(` Unlocked public key: ${unlockedWorkspacePublicKey}`);
|
||||
console.log(` Workspace matches: ${workspaceMatches}`);
|
||||
console.log(` Public key matches: ${publicKeyMatches}`);
|
||||
console.log(` Overall match: ${keypaceMatches}`);
|
||||
|
||||
// Always get current pending requests (filtered by connected workspace)
|
||||
const currentPendingRequests = this.getPendingRequests();
|
||||
|
||||
// Notify popup about keyspace state
|
||||
console.log(`Sending KEYSPACE_UNLOCKED message to popup: keypaceMatches=${keypaceMatches}, pendingRequests=${currentPendingRequests.length}`);
|
||||
if (this.popupPort) {
|
||||
this.popupPort.postMessage({
|
||||
type: 'KEYSPACE_UNLOCKED',
|
||||
keypaceMatches,
|
||||
pendingRequests: currentPendingRequests
|
||||
});
|
||||
console.log('KEYSPACE_UNLOCKED message sent to popup');
|
||||
} else {
|
||||
console.log('No popup port available to send KEYSPACE_UNLOCKED message');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.message && error.message.includes('Workspace not found')) {
|
||||
console.log(`Keyspace unlock: Different workspace unlocked (connected to: ${this.currentWorkspace})`);
|
||||
|
||||
// Send message with no match and empty requests
|
||||
if (this.popupPort) {
|
||||
this.popupPort.postMessage({
|
||||
type: 'KEYSPACE_UNLOCKED',
|
||||
keypaceMatches: false,
|
||||
pendingRequests: []
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.error('Error handling keyspace unlock:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve a sign request
|
||||
* @param {string} requestId - Request to approve
|
||||
* @returns {Promise<boolean>} - True if approved successfully
|
||||
*/
|
||||
async approveSignRequest(requestId) {
|
||||
try {
|
||||
const request = this.pendingRequests.get(requestId);
|
||||
if (!request) {
|
||||
throw new Error('Request not found');
|
||||
}
|
||||
|
||||
// Validate request is for current workspace
|
||||
if (request.workspace !== this.currentWorkspace) {
|
||||
throw new Error(`Request is for workspace '${request.workspace}', but current workspace is '${this.currentWorkspace}'`);
|
||||
}
|
||||
|
||||
if (request.connectedPublicKey !== this.connectedPublicKey) {
|
||||
throw new Error('Request public key does not match current connection');
|
||||
}
|
||||
|
||||
// Validate keyspace is still unlocked and matches
|
||||
if (!this.wasmModule.is_unlocked()) {
|
||||
throw new Error('Keyspace is locked');
|
||||
}
|
||||
|
||||
const currentPublicKey = await this.wasmModule.get_workspace_default_public_key(this.currentWorkspace);
|
||||
if (currentPublicKey !== this.connectedPublicKey) {
|
||||
throw new Error('Keyspace mismatch');
|
||||
}
|
||||
|
||||
// Decode message from base64
|
||||
const messageBytes = atob(request.message).split('').map(c => c.charCodeAt(0));
|
||||
|
||||
// Sign the message with default keypair (doesn't require selected keypair)
|
||||
const signatureHex = await this.wasmModule.sign_with_default_keypair(new Uint8Array(messageBytes));
|
||||
|
||||
// Send response to server
|
||||
await this.connection.send_response(requestId, request.message, signatureHex);
|
||||
|
||||
// Update request status
|
||||
request.status = 'approved';
|
||||
request.signature = signatureHex;
|
||||
|
||||
// Remove from pending requests
|
||||
this.pendingRequests.delete(requestId);
|
||||
|
||||
// Update badge
|
||||
this.updateBadge();
|
||||
|
||||
console.log(`Approved sign request: ${requestId}`);
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to approve sign request:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject a sign request
|
||||
* @param {string} requestId - Request to reject
|
||||
* @param {string} reason - Reason for rejection (optional)
|
||||
* @returns {Promise<boolean>} - True if rejected successfully
|
||||
*/
|
||||
async rejectSignRequest(requestId, reason = 'User rejected') {
|
||||
try {
|
||||
const request = this.pendingRequests.get(requestId);
|
||||
if (!request) {
|
||||
throw new Error('Request not found');
|
||||
}
|
||||
|
||||
// Send rejection to server
|
||||
await this.connection.send_rejection(requestId, reason);
|
||||
|
||||
// Update request status
|
||||
request.status = 'rejected';
|
||||
request.reason = reason;
|
||||
|
||||
// Remove from pending requests
|
||||
this.pendingRequests.delete(requestId);
|
||||
|
||||
// Update badge
|
||||
this.updateBadge();
|
||||
|
||||
console.log(`Rejected sign request: ${requestId}, reason: ${reason}`);
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to reject sign request:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all pending requests for the current workspace
|
||||
* @returns {Array} - Array of pending request data for current workspace
|
||||
*/
|
||||
getPendingRequests() {
|
||||
const allRequests = Array.from(this.pendingRequests.values());
|
||||
|
||||
// Filter requests to only include those for the current workspace
|
||||
const filteredRequests = allRequests.filter(request => {
|
||||
const isCurrentWorkspace = request.workspace === this.currentWorkspace;
|
||||
const isCurrentPublicKey = request.connectedPublicKey === this.connectedPublicKey;
|
||||
|
||||
if (!isCurrentWorkspace || !isCurrentPublicKey) {
|
||||
console.log(`Filtering out request ${request.id}: workspace=${request.workspace} (current=${this.currentWorkspace}), publicKey match=${isCurrentPublicKey}`);
|
||||
}
|
||||
|
||||
return isCurrentWorkspace && isCurrentPublicKey;
|
||||
});
|
||||
|
||||
console.log(`getPendingRequests: ${allRequests.length} total, ${filteredRequests.length} for current workspace`);
|
||||
return filteredRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show notification for new sign request
|
||||
*/
|
||||
showSignRequestNotification() {
|
||||
// Create notification
|
||||
chrome.notifications.create({
|
||||
type: 'basic',
|
||||
iconUrl: 'icons/icon48.png',
|
||||
title: 'Sign Request',
|
||||
message: 'New signature request received. Click to review.'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify popup about new request if popup is open and keyspace is unlocked
|
||||
*/
|
||||
async notifyPopupOfNewRequest() {
|
||||
// Only notify if popup is connected
|
||||
if (!this.popupPort) {
|
||||
console.log('No popup port available, skipping new request notification');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we have WASM module and can validate keyspace
|
||||
if (!this.wasmModule) {
|
||||
console.log('WASM module not available, skipping new request notification');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if keyspace is unlocked
|
||||
if (!this.wasmModule.is_unlocked()) {
|
||||
console.log('Keyspace is locked, skipping new request notification');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the currently unlocked workspace name
|
||||
const unlockedWorkspaceName = this.wasmModule.get_current_keyspace_name();
|
||||
|
||||
// Get workspace default public key for the UNLOCKED workspace
|
||||
const unlockedWorkspacePublicKey = await this.wasmModule.get_workspace_default_public_key(unlockedWorkspaceName);
|
||||
|
||||
// Check if the unlocked workspace matches the connected workspace
|
||||
const workspaceMatches = unlockedWorkspaceName === this.currentWorkspace;
|
||||
const publicKeyMatches = unlockedWorkspacePublicKey === this.connectedPublicKey;
|
||||
const keypaceMatches = workspaceMatches && publicKeyMatches;
|
||||
|
||||
console.log(`New request notification check: keypaceMatches=${keypaceMatches}, workspace=${unlockedWorkspaceName}, connected=${this.currentWorkspace}`);
|
||||
|
||||
// Get current pending requests (filtered by connected workspace)
|
||||
const currentPendingRequests = this.getPendingRequests();
|
||||
|
||||
// SECURITY: Only send requests if workspace matches, otherwise send empty array
|
||||
const requestsToSend = keypaceMatches ? currentPendingRequests : [];
|
||||
|
||||
// Send update to popup
|
||||
this.popupPort.postMessage({
|
||||
type: 'NEW_SIGN_REQUEST',
|
||||
keypaceMatches,
|
||||
pendingRequests: requestsToSend
|
||||
});
|
||||
|
||||
console.log(`Sent NEW_SIGN_REQUEST message to popup: keypaceMatches=${keypaceMatches}, ${requestsToSend.length} requests (${currentPendingRequests.length} total for connected workspace)`);
|
||||
|
||||
} catch (error) {
|
||||
console.log('Error in notifyPopupOfNewRequest:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update extension badge with pending request count for current workspace
|
||||
*/
|
||||
updateBadge() {
|
||||
// Only count requests for the current workspace
|
||||
const currentWorkspaceRequests = this.getPendingRequests();
|
||||
const count = currentWorkspaceRequests.length;
|
||||
const badgeText = count > 0 ? count.toString() : '';
|
||||
|
||||
console.log(`Updating badge: ${this.pendingRequests.size} total requests, ${count} for current workspace, badge text: "${badgeText}"`);
|
||||
|
||||
chrome.action.setBadgeText({ text: badgeText });
|
||||
chrome.action.setBadgeBackgroundColor({ color: '#ff6b6b' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from SigSocket server
|
||||
*/
|
||||
disconnect() {
|
||||
if (this.connection) {
|
||||
this.connection.disconnect();
|
||||
this.connection = null;
|
||||
}
|
||||
|
||||
this.isConnected = false;
|
||||
this.connectedPublicKey = null;
|
||||
this.currentWorkspace = null;
|
||||
this.pendingRequests.clear();
|
||||
this.updateBadge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connection status
|
||||
* @returns {Object} - Connection status information
|
||||
*/
|
||||
getStatus() {
|
||||
return {
|
||||
isConnected: this.isConnected,
|
||||
connectedPublicKey: this.connectedPublicKey,
|
||||
pendingRequestCount: this.getPendingRequests().length,
|
||||
serverUrl: this.defaultServerUrl
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the popup port for communication
|
||||
* @param {chrome.runtime.Port} port - The popup port
|
||||
*/
|
||||
setPopupPort(port) {
|
||||
this.popupPort = port;
|
||||
}
|
||||
}
|
||||
|
||||
// Export for use in background script
|
||||
export default SigSocketService;
|
75
crypto_vault_extension/demo/README.md
Normal file
75
crypto_vault_extension/demo/README.md
Normal file
@ -0,0 +1,75 @@
|
||||
# Mock SigSocket Server Demo
|
||||
|
||||
This directory contains a mock SigSocket server for testing the browser extension functionality.
|
||||
|
||||
## Setup
|
||||
|
||||
1. Install dependencies:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Start the mock server:
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
The server will listen on `ws://localhost:8080/ws`
|
||||
|
||||
## Usage
|
||||
|
||||
### Interactive Commands
|
||||
|
||||
Once the server is running, you can use these commands:
|
||||
|
||||
- `test` - Send a test sign request to all connected clients
|
||||
- `status` - Show server status and connected clients
|
||||
- `quit` - Shutdown the server
|
||||
|
||||
### Testing Flow
|
||||
|
||||
1. Start the mock server
|
||||
2. Load the browser extension in Chrome
|
||||
3. Create a keyspace and keypair in the extension
|
||||
4. The extension should automatically connect to the server
|
||||
5. The server will send a test sign request after 3 seconds
|
||||
6. Use the extension popup to approve or reject the request
|
||||
7. The server will log the response and send another request after 10 seconds
|
||||
|
||||
### Expected Output
|
||||
|
||||
When a client connects:
|
||||
```
|
||||
New WebSocket connection from: ::1
|
||||
Received message: 04a8b2c3d4e5f6...
|
||||
Client registered: client_1234567890_abc123 with public key: 04a8b2c3d4e5f6...
|
||||
📝 Sending sign request to client_1234567890_abc123: req_1_1234567890
|
||||
Message: "Test message 1 - 2024-01-01T12:00:00.000Z"
|
||||
```
|
||||
|
||||
When a sign response is received:
|
||||
```
|
||||
Received sign response from client_1234567890_abc123: {
|
||||
id: 'req_1_1234567890',
|
||||
message: 'VGVzdCBtZXNzYWdlIDEgLSAyMDI0LTAxLTAxVDEyOjAwOjAwLjAwMFo=',
|
||||
signature: '3045022100...'
|
||||
}
|
||||
✅ Sign request req_1_1234567890 completed successfully
|
||||
Signature: 3045022100...
|
||||
```
|
||||
|
||||
## Protocol
|
||||
|
||||
The mock server implements a simplified version of the SigSocket protocol:
|
||||
|
||||
1. **Client Introduction**: Client sends hex-encoded public key
|
||||
2. **Welcome Message**: Server responds with welcome JSON
|
||||
3. **Sign Requests**: Server sends JSON with `id` and `message` (base64)
|
||||
4. **Sign Responses**: Client sends JSON with `id`, `message`, and `signature`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Connection refused**: Make sure the server is running on port 8080
|
||||
- **No sign requests**: Check that the extension is properly connected
|
||||
- **Extension errors**: Check the browser console for JavaScript errors
|
||||
- **WASM errors**: Ensure the WASM files are properly built and loaded
|
232
crypto_vault_extension/demo/mock_sigsocket_server.js
Normal file
232
crypto_vault_extension/demo/mock_sigsocket_server.js
Normal file
@ -0,0 +1,232 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Mock SigSocket Server for Testing Browser Extension
|
||||
*
|
||||
* This is a simple WebSocket server that simulates the SigSocket protocol
|
||||
* for testing the browser extension functionality.
|
||||
*
|
||||
* Usage:
|
||||
* node mock_sigsocket_server.js
|
||||
*
|
||||
* The server will listen on ws://localhost:8080/ws
|
||||
*/
|
||||
|
||||
const WebSocket = require('ws');
|
||||
const http = require('http');
|
||||
|
||||
class MockSigSocketServer {
|
||||
constructor(port = 8080) {
|
||||
this.port = port;
|
||||
this.clients = new Map(); // clientId -> { ws, publicKey }
|
||||
this.requestCounter = 0;
|
||||
|
||||
this.setupServer();
|
||||
}
|
||||
|
||||
setupServer() {
|
||||
// Create HTTP server
|
||||
this.httpServer = http.createServer();
|
||||
|
||||
// Create WebSocket server
|
||||
this.wss = new WebSocket.Server({
|
||||
server: this.httpServer,
|
||||
path: '/ws'
|
||||
});
|
||||
|
||||
this.wss.on('connection', (ws, req) => {
|
||||
console.log('New WebSocket connection from:', req.socket.remoteAddress);
|
||||
this.handleConnection(ws);
|
||||
});
|
||||
|
||||
this.httpServer.listen(this.port, () => {
|
||||
console.log(`Mock SigSocket Server listening on ws://localhost:${this.port}/ws`);
|
||||
console.log('Waiting for browser extension connections...');
|
||||
});
|
||||
}
|
||||
|
||||
handleConnection(ws) {
|
||||
let clientId = null;
|
||||
let publicKey = null;
|
||||
|
||||
ws.on('message', (data) => {
|
||||
try {
|
||||
const message = data.toString();
|
||||
console.log('Received message:', message);
|
||||
|
||||
// Check if this is a client introduction (hex-encoded public key)
|
||||
if (!clientId && this.isHexString(message)) {
|
||||
publicKey = message;
|
||||
clientId = this.generateClientId();
|
||||
|
||||
this.clients.set(clientId, { ws, publicKey });
|
||||
|
||||
console.log(`Client registered: ${clientId} with public key: ${publicKey.substring(0, 16)}...`);
|
||||
|
||||
// Send welcome message
|
||||
ws.send(JSON.stringify({
|
||||
type: 'welcome',
|
||||
clientId: clientId,
|
||||
message: 'Connected to Mock SigSocket Server'
|
||||
}));
|
||||
|
||||
// Schedule a test sign request after 3 seconds
|
||||
setTimeout(() => {
|
||||
this.sendTestSignRequest(clientId);
|
||||
}, 3000);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to parse as JSON (sign response)
|
||||
try {
|
||||
const jsonMessage = JSON.parse(message);
|
||||
this.handleSignResponse(clientId, jsonMessage);
|
||||
} catch (e) {
|
||||
console.log('Received non-JSON message:', message);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error handling message:', error);
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('close', () => {
|
||||
if (clientId) {
|
||||
this.clients.delete(clientId);
|
||||
console.log(`Client disconnected: ${clientId}`);
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('error', (error) => {
|
||||
console.error('WebSocket error:', error);
|
||||
});
|
||||
}
|
||||
|
||||
handleSignResponse(clientId, response) {
|
||||
console.log(`Received sign response from ${clientId}:`, response);
|
||||
|
||||
if (response.id && response.signature) {
|
||||
console.log(`✅ Sign request ${response.id} completed successfully`);
|
||||
console.log(` Signature: ${response.signature.substring(0, 32)}...`);
|
||||
|
||||
// Send another test request after 10 seconds
|
||||
setTimeout(() => {
|
||||
this.sendTestSignRequest(clientId);
|
||||
}, 10000);
|
||||
} else {
|
||||
console.log('❌ Invalid sign response format');
|
||||
}
|
||||
}
|
||||
|
||||
sendTestSignRequest(clientId) {
|
||||
const client = this.clients.get(clientId);
|
||||
if (!client) {
|
||||
console.log(`Client ${clientId} not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.requestCounter++;
|
||||
const requestId = `req_${this.requestCounter}_${Date.now()}`;
|
||||
const testMessage = `Test message ${this.requestCounter} - ${new Date().toISOString()}`;
|
||||
const messageBase64 = Buffer.from(testMessage).toString('base64');
|
||||
|
||||
const signRequest = {
|
||||
id: requestId,
|
||||
message: messageBase64
|
||||
};
|
||||
|
||||
console.log(`📝 Sending sign request to ${clientId}:`, requestId);
|
||||
console.log(` Message: "${testMessage}"`);
|
||||
|
||||
try {
|
||||
client.ws.send(JSON.stringify(signRequest));
|
||||
} catch (error) {
|
||||
console.error(`Failed to send sign request to ${clientId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
isHexString(str) {
|
||||
return /^[0-9a-fA-F]+$/.test(str) && str.length >= 32; // At least 16 bytes
|
||||
}
|
||||
|
||||
generateClientId() {
|
||||
return `client_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
|
||||
}
|
||||
|
||||
// Send a test request to all connected clients
|
||||
broadcastTestRequest() {
|
||||
console.log('\n📢 Broadcasting test sign request to all clients...');
|
||||
for (const [clientId] of this.clients) {
|
||||
this.sendTestSignRequest(clientId);
|
||||
}
|
||||
}
|
||||
|
||||
// Get server status
|
||||
getStatus() {
|
||||
return {
|
||||
port: this.port,
|
||||
connectedClients: this.clients.size,
|
||||
clients: Array.from(this.clients.keys())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Create and start the server
|
||||
const server = new MockSigSocketServer();
|
||||
|
||||
// Handle graceful shutdown
|
||||
process.on('SIGINT', () => {
|
||||
console.log('\n🛑 Shutting down Mock SigSocket Server...');
|
||||
server.httpServer.close(() => {
|
||||
console.log('Server closed');
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
// Add some interactive commands
|
||||
process.stdin.setEncoding('utf8');
|
||||
console.log('\n📋 Available commands:');
|
||||
console.log(' "test" - Send test sign request to all clients');
|
||||
console.log(' "status" - Show server status');
|
||||
console.log(' "quit" - Shutdown server');
|
||||
console.log(' Type a command and press Enter\n');
|
||||
|
||||
process.stdin.on('readable', () => {
|
||||
const chunk = process.stdin.read();
|
||||
if (chunk !== null) {
|
||||
const command = chunk.trim().toLowerCase();
|
||||
|
||||
switch (command) {
|
||||
case 'test':
|
||||
server.broadcastTestRequest();
|
||||
break;
|
||||
|
||||
case 'status':
|
||||
const status = server.getStatus();
|
||||
console.log('\n📊 Server Status:');
|
||||
console.log(` Port: ${status.port}`);
|
||||
console.log(` Connected clients: ${status.connectedClients}`);
|
||||
if (status.clients.length > 0) {
|
||||
console.log(` Client IDs: ${status.clients.join(', ')}`);
|
||||
}
|
||||
console.log('');
|
||||
break;
|
||||
|
||||
case 'quit':
|
||||
case 'exit':
|
||||
process.emit('SIGINT');
|
||||
break;
|
||||
|
||||
case '':
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`Unknown command: ${command}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Export for testing
|
||||
module.exports = MockSigSocketServer;
|
21
crypto_vault_extension/demo/package.json
Normal file
21
crypto_vault_extension/demo/package.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "mock-sigsocket-server",
|
||||
"version": "1.0.0",
|
||||
"description": "Mock SigSocket server for testing browser extension",
|
||||
"main": "mock_sigsocket_server.js",
|
||||
"scripts": {
|
||||
"start": "node mock_sigsocket_server.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ws": "^8.14.0"
|
||||
},
|
||||
"keywords": [
|
||||
"websocket",
|
||||
"sigsocket",
|
||||
"testing",
|
||||
"mock"
|
||||
],
|
||||
"author": "",
|
||||
"license": "MIT"
|
||||
}
|
@ -6,7 +6,8 @@
|
||||
|
||||
"permissions": [
|
||||
"storage",
|
||||
"activeTab"
|
||||
"activeTab",
|
||||
"notifications"
|
||||
],
|
||||
|
||||
"icons": {
|
||||
|
@ -27,6 +27,10 @@
|
||||
<span>seconds</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-item">
|
||||
<label for="sigSocketUrlInput">SigSocket Server</label>
|
||||
<input type="text" id="sigSocketUrlInput" placeholder="ws://localhost:8080/ws" value="ws://localhost:8080/ws">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="themeToggle" class="btn-icon-only" title="Switch to dark mode">
|
||||
@ -189,6 +193,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sign Request Manager -->
|
||||
<div id="signRequestContainer">
|
||||
<!-- Sign request manager will be rendered here -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@ -198,6 +207,7 @@
|
||||
|
||||
<!-- Enhanced JavaScript modules -->
|
||||
<script src="js/errorHandler.js"></script>
|
||||
<script src="popup/components/SignRequestManager.js"></script>
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -129,6 +129,7 @@ const elements = {
|
||||
settingsToggle: document.getElementById('settingsToggle'),
|
||||
settingsDropdown: document.getElementById('settingsDropdown'),
|
||||
timeoutInput: document.getElementById('timeoutInput'),
|
||||
sigSocketUrlInput: document.getElementById('sigSocketUrlInput'),
|
||||
|
||||
// Keypair management elements
|
||||
toggleAddKeypairBtn: document.getElementById('toggleAddKeypairBtn'),
|
||||
@ -167,6 +168,7 @@ let currentKeyspace = null;
|
||||
let selectedKeypairId = null;
|
||||
let backgroundPort = null;
|
||||
let sessionTimeoutDuration = 15; // Default 15 seconds
|
||||
let signRequestManager = null; // Sign request manager instance
|
||||
|
||||
// Session timeout management
|
||||
function handleError(error, context, shouldShowToast = true) {
|
||||
@ -198,11 +200,14 @@ function validateInput(value, fieldName, options = {}) {
|
||||
return true;
|
||||
}
|
||||
async function loadTimeoutSetting() {
|
||||
const result = await chrome.storage.local.get(['sessionTimeout']);
|
||||
const result = await chrome.storage.local.get(['sessionTimeout', 'sigSocketUrl']);
|
||||
sessionTimeoutDuration = result.sessionTimeout || 15;
|
||||
if (elements.timeoutInput) {
|
||||
elements.timeoutInput.value = sessionTimeoutDuration;
|
||||
}
|
||||
if (elements.sigSocketUrlInput) {
|
||||
elements.sigSocketUrlInput.value = result.sigSocketUrl || 'ws://localhost:8080/ws';
|
||||
}
|
||||
}
|
||||
|
||||
async function checkSessionTimeout() {
|
||||
@ -307,6 +312,39 @@ function connectToBackground() {
|
||||
|
||||
// Show timeout notification
|
||||
showToast(message.message, 'info');
|
||||
|
||||
// Update sign request manager
|
||||
if (signRequestManager) {
|
||||
signRequestManager.updateState({ isKeypaceUnlocked: false });
|
||||
}
|
||||
} else if (message.type === 'KEYSPACE_UNLOCKED') {
|
||||
// Handle keyspace unlock from SigSocket service
|
||||
console.log('Popup received KEYSPACE_UNLOCKED message:', message);
|
||||
if (signRequestManager) {
|
||||
console.log('Updating SignRequestManager state with keypaceMatches:', message.keypaceMatches);
|
||||
signRequestManager.updateState({
|
||||
isKeypaceUnlocked: true,
|
||||
keypaceMatches: message.keypaceMatches,
|
||||
pendingRequests: message.pendingRequests
|
||||
});
|
||||
console.log('SignRequestManager state updated');
|
||||
} else {
|
||||
console.log('No SignRequestManager available to update');
|
||||
}
|
||||
} else if (message.type === 'NEW_SIGN_REQUEST') {
|
||||
// Handle new sign request when keyspace is already unlocked
|
||||
console.log('Popup received NEW_SIGN_REQUEST message:', message);
|
||||
if (signRequestManager) {
|
||||
console.log('Updating SignRequestManager with new request, keypaceMatches:', message.keypaceMatches);
|
||||
// Only update if we have a valid keyspace match, otherwise requests array will be empty for security
|
||||
signRequestManager.updateState({
|
||||
keypaceMatches: message.keypaceMatches,
|
||||
pendingRequests: message.pendingRequests
|
||||
});
|
||||
console.log(`SignRequestManager updated with new request: ${message.pendingRequests.length} requests visible`);
|
||||
} else {
|
||||
console.log('No SignRequestManager available to update with new request');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -315,6 +353,22 @@ function connectToBackground() {
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize Sign Request Manager
|
||||
async function initializeSignRequestManager() {
|
||||
try {
|
||||
const container = document.getElementById('signRequestContainer');
|
||||
if (container && window.SignRequestManager) {
|
||||
signRequestManager = new window.SignRequestManager();
|
||||
await signRequestManager.initialize(container);
|
||||
console.log('Sign Request Manager initialized');
|
||||
} else {
|
||||
console.warn('SignRequestManager not available or container not found');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize Sign Request Manager:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize
|
||||
document.addEventListener('DOMContentLoaded', async function() {
|
||||
// Initialize theme first
|
||||
@ -332,6 +386,9 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
// Connect to background script for keep-alive
|
||||
connectToBackground();
|
||||
|
||||
// Initialize Sign Request Manager
|
||||
await initializeSignRequestManager();
|
||||
|
||||
// Consolidated event listeners
|
||||
const eventMap = {
|
||||
createKeyspaceBtn: createKeyspace,
|
||||
@ -376,6 +433,18 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
}
|
||||
});
|
||||
|
||||
// SigSocket URL setting event listener
|
||||
elements.sigSocketUrlInput?.addEventListener('change', async (e) => {
|
||||
const url = e.target.value.trim();
|
||||
if (url) {
|
||||
await chrome.storage.local.set({ sigSocketUrl: url });
|
||||
// Update the SigSocket service with new URL if it exists
|
||||
if (signRequestManager && signRequestManager.sigSocketService) {
|
||||
signRequestManager.sigSocketService.defaultServerUrl = url;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Activity detection - reset timeout on any interaction
|
||||
document.addEventListener('click', (e) => {
|
||||
resetSessionTimeout();
|
||||
@ -644,6 +713,12 @@ async function login() {
|
||||
clearVaultState();
|
||||
await loadKeypairs();
|
||||
|
||||
// Notify sign request manager about keyspace unlock
|
||||
if (signRequestManager) {
|
||||
signRequestManager.updateState({ isKeypaceUnlocked: true });
|
||||
await signRequestManager.refresh();
|
||||
}
|
||||
|
||||
return response;
|
||||
} else {
|
||||
throw new Error(getResponseError(response, 'login'));
|
||||
@ -673,6 +748,11 @@ async function lockSession() {
|
||||
elements.passwordInput.value = '';
|
||||
clearVaultState();
|
||||
|
||||
// Update sign request manager
|
||||
if (signRequestManager) {
|
||||
signRequestManager.updateState({ isKeypaceUnlocked: false });
|
||||
}
|
||||
|
||||
showToast('Session locked', 'info');
|
||||
} catch (error) {
|
||||
showToast('Error: ' + error.message, 'error');
|
||||
|
443
crypto_vault_extension/popup/components/SignRequestManager.js
Normal file
443
crypto_vault_extension/popup/components/SignRequestManager.js
Normal file
@ -0,0 +1,443 @@
|
||||
/**
|
||||
* Sign Request Manager Component
|
||||
*
|
||||
* Handles the display and management of SigSocket sign requests in the popup.
|
||||
* Manages different UI states:
|
||||
* 1. Keyspace locked: Show unlock form
|
||||
* 2. Wrong keyspace: Show mismatch message
|
||||
* 3. Correct keyspace: Show approval UI
|
||||
*/
|
||||
|
||||
class SignRequestManager {
|
||||
constructor() {
|
||||
this.pendingRequests = [];
|
||||
this.isKeypaceUnlocked = false;
|
||||
this.keypaceMatch = false;
|
||||
this.connectionStatus = { isConnected: false };
|
||||
|
||||
this.container = null;
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the component
|
||||
* @param {HTMLElement} container - Container element to render into
|
||||
*/
|
||||
async initialize(container) {
|
||||
this.container = container;
|
||||
this.initialized = true;
|
||||
|
||||
// Load initial state
|
||||
await this.loadState();
|
||||
|
||||
// Render initial UI
|
||||
this.render();
|
||||
|
||||
// Set up event listeners
|
||||
this.setupEventListeners();
|
||||
|
||||
// Listen for background messages
|
||||
this.setupBackgroundListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load current state from background script
|
||||
*/
|
||||
async loadState() {
|
||||
try {
|
||||
// Check if keyspace is unlocked
|
||||
const unlockedResponse = await this.sendMessage('isUnlocked');
|
||||
this.isKeypaceUnlocked = unlockedResponse?.unlocked || false;
|
||||
|
||||
// Get pending requests
|
||||
const requestsResponse = await this.sendMessage('getPendingRequests');
|
||||
this.pendingRequests = requestsResponse?.requests || [];
|
||||
|
||||
// Get SigSocket status
|
||||
const statusResponse = await this.sendMessage('getSigSocketStatus');
|
||||
this.connectionStatus = statusResponse?.status || { isConnected: false };
|
||||
|
||||
// If keyspace is unlocked, notify background to check keyspace match
|
||||
if (this.isKeypaceUnlocked) {
|
||||
await this.sendMessage('keypaceUnlocked');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to load sign request state:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the component UI
|
||||
*/
|
||||
render() {
|
||||
if (!this.container) return;
|
||||
|
||||
const hasRequests = this.pendingRequests.length > 0;
|
||||
|
||||
if (!hasRequests) {
|
||||
this.renderNoRequests();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isKeypaceUnlocked) {
|
||||
this.renderUnlockPrompt();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.keypaceMatch) {
|
||||
this.renderKeypaceMismatch();
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderApprovalUI();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render no requests state
|
||||
*/
|
||||
renderNoRequests() {
|
||||
this.container.innerHTML = `
|
||||
<div class="sign-request-manager">
|
||||
<div class="connection-status ${this.connectionStatus.isConnected ? 'connected' : 'disconnected'}">
|
||||
<span class="status-indicator"></span>
|
||||
SigSocket: ${this.connectionStatus.isConnected ? 'Connected' : 'Disconnected'}
|
||||
</div>
|
||||
<div class="no-requests">
|
||||
<p>No pending sign requests</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render unlock prompt
|
||||
*/
|
||||
renderUnlockPrompt() {
|
||||
const requestCount = this.pendingRequests.length;
|
||||
this.container.innerHTML = `
|
||||
<div class="sign-request-manager">
|
||||
<div class="connection-status ${this.connectionStatus.isConnected ? 'connected' : 'disconnected'}">
|
||||
<span class="status-indicator"></span>
|
||||
SigSocket: ${this.connectionStatus.isConnected ? 'Connected' : 'Disconnected'}
|
||||
</div>
|
||||
<div class="unlock-prompt">
|
||||
<h3>🔒 Unlock Keyspace</h3>
|
||||
<p>Unlock your keyspace to see ${requestCount} pending sign request${requestCount !== 1 ? 's' : ''}.</p>
|
||||
<p class="hint">Use the login form above to unlock your keyspace.</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render keyspace mismatch message
|
||||
*/
|
||||
renderKeypaceMismatch() {
|
||||
this.container.innerHTML = `
|
||||
<div class="sign-request-manager">
|
||||
<div class="connection-status ${this.connectionStatus.isConnected ? 'connected' : 'disconnected'}">
|
||||
<span class="status-indicator"></span>
|
||||
SigSocket: ${this.connectionStatus.isConnected ? 'Connected' : 'Disconnected'}
|
||||
</div>
|
||||
<div class="keyspace-mismatch">
|
||||
<h3>⚠️ Wrong Keyspace</h3>
|
||||
<p>The unlocked keyspace doesn't match the connected SigSocket session.</p>
|
||||
<p class="hint">Please unlock the correct keyspace to approve sign requests.</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render approval UI with pending requests
|
||||
*/
|
||||
renderApprovalUI() {
|
||||
const requestsHtml = this.pendingRequests.map(request => this.renderSignRequestCard(request)).join('');
|
||||
|
||||
this.container.innerHTML = `
|
||||
<div class="sign-request-manager">
|
||||
<div class="connection-status connected">
|
||||
<span class="status-indicator"></span>
|
||||
SigSocket: Connected
|
||||
</div>
|
||||
<div class="requests-header">
|
||||
<h3>📝 Sign Requests (${this.pendingRequests.length})</h3>
|
||||
</div>
|
||||
<div class="requests-list">
|
||||
${requestsHtml}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render individual sign request card
|
||||
* @param {Object} request - Sign request data
|
||||
* @returns {string} - HTML string for the request card
|
||||
*/
|
||||
renderSignRequestCard(request) {
|
||||
const timestamp = new Date(request.timestamp).toLocaleTimeString();
|
||||
const messagePreview = this.getMessagePreview(request.message);
|
||||
|
||||
return `
|
||||
<div class="sign-request-card" data-request-id="${request.id}">
|
||||
<div class="request-header">
|
||||
<div class="request-id">Request: ${request.id.substring(0, 8)}...</div>
|
||||
<div class="request-time">${timestamp}</div>
|
||||
</div>
|
||||
<div class="request-message">
|
||||
<label>Message:</label>
|
||||
<div class="message-content">
|
||||
<div class="message-preview">${messagePreview}</div>
|
||||
<button class="expand-message" data-request-id="${request.id}">
|
||||
<span class="expand-text">Show Full</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="request-actions">
|
||||
<button class="btn-reject" data-request-id="${request.id}">
|
||||
❌ Reject
|
||||
</button>
|
||||
<button class="btn-approve" data-request-id="${request.id}">
|
||||
✅ Approve & Sign
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a preview of the message content
|
||||
* @param {string} messageBase64 - Base64 encoded message
|
||||
* @returns {string} - Preview text
|
||||
*/
|
||||
getMessagePreview(messageBase64) {
|
||||
try {
|
||||
const decoded = atob(messageBase64);
|
||||
const preview = decoded.length > 50 ? decoded.substring(0, 50) + '...' : decoded;
|
||||
return preview;
|
||||
} catch (error) {
|
||||
return `Base64: ${messageBase64.substring(0, 20)}...`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up event listeners
|
||||
*/
|
||||
setupEventListeners() {
|
||||
if (!this.container) return;
|
||||
|
||||
// Use event delegation for dynamic content
|
||||
this.container.addEventListener('click', (e) => {
|
||||
const target = e.target;
|
||||
|
||||
if (target.classList.contains('btn-approve')) {
|
||||
const requestId = target.getAttribute('data-request-id');
|
||||
this.approveRequest(requestId);
|
||||
} else if (target.classList.contains('btn-reject')) {
|
||||
const requestId = target.getAttribute('data-request-id');
|
||||
this.rejectRequest(requestId);
|
||||
} else if (target.classList.contains('expand-message')) {
|
||||
const requestId = target.getAttribute('data-request-id');
|
||||
this.toggleMessageExpansion(requestId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up listener for background script messages
|
||||
*/
|
||||
setupBackgroundListener() {
|
||||
// Listen for keyspace unlock events
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.type === 'KEYSPACE_UNLOCKED') {
|
||||
this.isKeypaceUnlocked = true;
|
||||
this.keypaceMatch = message.keypaceMatches;
|
||||
this.pendingRequests = message.pendingRequests || [];
|
||||
this.render();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve a sign request
|
||||
* @param {string} requestId - Request ID to approve
|
||||
*/
|
||||
async approveRequest(requestId) {
|
||||
try {
|
||||
const button = this.container.querySelector(`[data-request-id="${requestId}"].btn-approve`);
|
||||
if (button) {
|
||||
button.disabled = true;
|
||||
button.textContent = 'Signing...';
|
||||
}
|
||||
|
||||
const response = await this.sendMessage('approveSignRequest', { requestId });
|
||||
|
||||
if (response?.success) {
|
||||
// Remove the request from UI
|
||||
this.pendingRequests = this.pendingRequests.filter(r => r.id !== requestId);
|
||||
this.render();
|
||||
|
||||
// Show success message
|
||||
this.showToast('Sign request approved successfully!', 'success');
|
||||
} else {
|
||||
throw new Error(response?.error || 'Failed to approve request');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to approve request:', error);
|
||||
this.showToast('Failed to approve request: ' + error.message, 'error');
|
||||
|
||||
// Re-enable button
|
||||
const button = this.container.querySelector(`[data-request-id="${requestId}"].btn-approve`);
|
||||
if (button) {
|
||||
button.disabled = false;
|
||||
button.textContent = '✅ Approve & Sign';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject a sign request
|
||||
* @param {string} requestId - Request ID to reject
|
||||
*/
|
||||
async rejectRequest(requestId) {
|
||||
try {
|
||||
const button = this.container.querySelector(`[data-request-id="${requestId}"].btn-reject`);
|
||||
if (button) {
|
||||
button.disabled = true;
|
||||
button.textContent = 'Rejecting...';
|
||||
}
|
||||
|
||||
const response = await this.sendMessage('rejectSignRequest', {
|
||||
requestId,
|
||||
reason: 'User rejected'
|
||||
});
|
||||
|
||||
if (response?.success) {
|
||||
// Remove the request from UI
|
||||
this.pendingRequests = this.pendingRequests.filter(r => r.id !== requestId);
|
||||
this.render();
|
||||
|
||||
// Show success message
|
||||
this.showToast('Sign request rejected', 'info');
|
||||
} else {
|
||||
throw new Error(response?.error || 'Failed to reject request');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to reject request:', error);
|
||||
this.showToast('Failed to reject request: ' + error.message, 'error');
|
||||
|
||||
// Re-enable button
|
||||
const button = this.container.querySelector(`[data-request-id="${requestId}"].btn-reject`);
|
||||
if (button) {
|
||||
button.disabled = false;
|
||||
button.textContent = '❌ Reject';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle message expansion
|
||||
* @param {string} requestId - Request ID
|
||||
*/
|
||||
toggleMessageExpansion(requestId) {
|
||||
const request = this.pendingRequests.find(r => r.id === requestId);
|
||||
if (!request) return;
|
||||
|
||||
const card = this.container.querySelector(`[data-request-id="${requestId}"]`);
|
||||
const messageContent = card.querySelector('.message-content');
|
||||
const expandButton = card.querySelector('.expand-message');
|
||||
|
||||
const isExpanded = messageContent.classList.contains('expanded');
|
||||
|
||||
if (isExpanded) {
|
||||
messageContent.classList.remove('expanded');
|
||||
messageContent.querySelector('.message-preview').textContent = this.getMessagePreview(request.message);
|
||||
expandButton.querySelector('.expand-text').textContent = 'Show Full';
|
||||
} else {
|
||||
messageContent.classList.add('expanded');
|
||||
try {
|
||||
const fullMessage = atob(request.message);
|
||||
messageContent.querySelector('.message-preview').textContent = fullMessage;
|
||||
} catch (error) {
|
||||
messageContent.querySelector('.message-preview').textContent = `Base64: ${request.message}`;
|
||||
}
|
||||
expandButton.querySelector('.expand-text').textContent = 'Show Less';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send message to background script
|
||||
* @param {string} action - Action to perform
|
||||
* @param {Object} data - Additional data
|
||||
* @returns {Promise<Object>} - Response from background script
|
||||
*/
|
||||
async sendMessage(action, data = {}) {
|
||||
return new Promise((resolve) => {
|
||||
chrome.runtime.sendMessage({ action, ...data }, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show toast notification
|
||||
* @param {string} message - Message to show
|
||||
* @param {string} type - Toast type (success, error, info)
|
||||
*/
|
||||
showToast(message, type = 'info') {
|
||||
// Use the existing toast system from popup.js
|
||||
if (typeof showToast === 'function') {
|
||||
showToast(message, type);
|
||||
} else {
|
||||
console.log(`[${type.toUpperCase()}] ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update component state
|
||||
* @param {Object} newState - New state data
|
||||
*/
|
||||
updateState(newState) {
|
||||
console.log('SignRequestManager.updateState called with:', newState);
|
||||
console.log('Current state before update:', {
|
||||
isKeypaceUnlocked: this.isKeypaceUnlocked,
|
||||
keypaceMatch: this.keypaceMatch,
|
||||
pendingRequests: this.pendingRequests.length
|
||||
});
|
||||
|
||||
Object.assign(this, newState);
|
||||
|
||||
// Fix the property name mismatch
|
||||
if (newState.keypaceMatches !== undefined) {
|
||||
this.keypaceMatch = newState.keypaceMatches;
|
||||
}
|
||||
|
||||
console.log('State after update:', {
|
||||
isKeypaceUnlocked: this.isKeypaceUnlocked,
|
||||
keypaceMatch: this.keypaceMatch,
|
||||
pendingRequests: this.pendingRequests.length
|
||||
});
|
||||
|
||||
if (this.initialized) {
|
||||
console.log('Rendering SignRequestManager with new state');
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh component data
|
||||
*/
|
||||
async refresh() {
|
||||
await this.loadState();
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
// Export for use in popup
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = SignRequestManager;
|
||||
} else {
|
||||
window.SignRequestManager = SignRequestManager;
|
||||
}
|
@ -1070,3 +1070,194 @@ input::placeholder, textarea::placeholder {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* Sign Request Manager Styles */
|
||||
.sign-request-manager {
|
||||
margin-top: 16px;
|
||||
padding: 16px;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.connection-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #ef4444;
|
||||
}
|
||||
|
||||
.connection-status.connected .status-indicator {
|
||||
background: #10b981;
|
||||
}
|
||||
|
||||
.no-requests {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.unlock-prompt, .keyspace-mismatch {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.unlock-prompt h3, .keyspace-mismatch h3 {
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 16px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.unlock-prompt p, .keyspace-mismatch p {
|
||||
margin: 8px 0;
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-style: italic;
|
||||
font-size: 12px !important;
|
||||
color: var(--text-tertiary) !important;
|
||||
}
|
||||
|
||||
.requests-header {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.requests-header h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.requests-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.sign-request-card {
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.request-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.request-id {
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.request-time {
|
||||
font-size: 11px;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.request-message {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.request-message label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.message-preview {
|
||||
font-family: monospace;
|
||||
font-size: 11px;
|
||||
color: var(--text-primary);
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
max-height: 60px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.message-content.expanded .message-preview {
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.expand-message {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
background: var(--accent-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
padding: 2px 6px;
|
||||
font-size: 10px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.expand-message:hover {
|
||||
background: var(--accent-hover);
|
||||
}
|
||||
|
||||
.request-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.btn-approve, .btn-reject {
|
||||
padding: 6px 12px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn-approve {
|
||||
background: #10b981;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-approve:hover:not(:disabled) {
|
||||
background: #059669;
|
||||
}
|
||||
|
||||
.btn-reject {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-reject:hover:not(:disabled) {
|
||||
background: #dc2626;
|
||||
}
|
||||
|
||||
.btn-approve:disabled, .btn-reject:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
114
crypto_vault_extension/test_extension.md
Normal file
114
crypto_vault_extension/test_extension.md
Normal file
@ -0,0 +1,114 @@
|
||||
# Testing the SigSocket Browser Extension
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **SigSocket Server**: You need a running SigSocket server at `ws://localhost:8080/ws`
|
||||
2. **Browser**: Chrome or Chromium-based browser with developer mode enabled
|
||||
|
||||
## Test Steps
|
||||
|
||||
### 1. Load the Extension
|
||||
|
||||
1. Open Chrome and go to `chrome://extensions/`
|
||||
2. Enable "Developer mode" in the top right
|
||||
3. Click "Load unpacked" and select the `crypto_vault_extension` directory
|
||||
4. The CryptoVault extension should appear in your extensions list
|
||||
|
||||
### 2. Basic Functionality Test
|
||||
|
||||
1. Click the CryptoVault extension icon in the toolbar
|
||||
2. Create a new keyspace:
|
||||
- Enter a keyspace name (e.g., "test-workspace")
|
||||
- Enter a password
|
||||
- Click "Create New"
|
||||
3. The extension should automatically connect to the SigSocket server
|
||||
4. Add a keypair:
|
||||
- Click "Add Keypair"
|
||||
- Enter a name for the keypair
|
||||
- Click "Create Keypair"
|
||||
|
||||
### 3. SigSocket Integration Test
|
||||
|
||||
1. **Check Connection Status**:
|
||||
- Look for the SigSocket connection status at the bottom of the popup
|
||||
- It should show "SigSocket: Connected" with a green indicator
|
||||
|
||||
2. **Test Sign Request Flow**:
|
||||
- Send a sign request to the SigSocket server (you'll need to implement this on the server side)
|
||||
- The extension should show a notification
|
||||
- The extension badge should show the number of pending requests
|
||||
- Open the extension popup to see the sign request
|
||||
|
||||
3. **Test Approval Flow**:
|
||||
- If keyspace is locked, you should see "Unlock keyspace to see X pending requests"
|
||||
- Unlock the keyspace using the login form
|
||||
- You should see the sign request details
|
||||
- Click "Approve & Sign" to approve the request
|
||||
- The request should be signed and sent back to the server
|
||||
|
||||
### 4. Settings Test
|
||||
|
||||
1. Click the settings gear icon in the extension popup
|
||||
2. Change the SigSocket server URL if needed
|
||||
3. Adjust the session timeout if desired
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
- ✅ Extension loads without errors
|
||||
- ✅ Can create keyspaces and keypairs
|
||||
- ✅ SigSocket connection is established automatically
|
||||
- ✅ Sign requests are received and displayed
|
||||
- ✅ Approval flow works correctly
|
||||
- ✅ Settings can be configured
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Extension won't load**: Check the console for JavaScript errors
|
||||
2. **SigSocket won't connect**: Verify the server is running and the URL is correct
|
||||
3. **WASM errors**: Check that the WASM files are properly built and copied
|
||||
4. **Sign requests not appearing**: Check the browser console for callback errors
|
||||
|
||||
### Debug Steps
|
||||
|
||||
1. Open Chrome DevTools
|
||||
2. Go to the Extensions tab
|
||||
3. Find CryptoVault and click "Inspect views: background page"
|
||||
4. Check the console for any errors
|
||||
5. Also inspect the popup by right-clicking the extension icon and selecting "Inspect popup"
|
||||
|
||||
## Server-Side Testing
|
||||
|
||||
To fully test the extension, you'll need a SigSocket server that can:
|
||||
|
||||
1. Accept WebSocket connections at `/ws`
|
||||
2. Handle client introduction messages (hex-encoded public keys)
|
||||
3. Send sign requests in the format:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "unique-request-id",
|
||||
"message": "base64-encoded-message"
|
||||
}
|
||||
```
|
||||
|
||||
4. Receive sign responses in the format:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "request-id",
|
||||
"message": "base64-encoded-message",
|
||||
"signature": "base64-encoded-signature"
|
||||
}
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
If basic functionality works:
|
||||
|
||||
1. Test with multiple concurrent sign requests
|
||||
2. Test connection recovery after network issues
|
||||
3. Test with different keyspace configurations
|
||||
4. Test the rejection flow
|
||||
5. Test session timeout behavior
|
@ -202,6 +202,33 @@ function debugString(val) {
|
||||
// TODO we could test for more things here, like `Set`s and `Map`s.
|
||||
return className;
|
||||
}
|
||||
/**
|
||||
* Initialize the scripting environment (must be called before run_rhai)
|
||||
*/
|
||||
export function init_rhai_env() {
|
||||
wasm.init_rhai_env();
|
||||
}
|
||||
|
||||
function takeFromExternrefTable0(idx) {
|
||||
const value = wasm.__wbindgen_export_2.get(idx);
|
||||
wasm.__externref_table_dealloc(idx);
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* Securely run a Rhai script in the extension context (must be called only after user approval)
|
||||
* @param {string} script
|
||||
* @returns {any}
|
||||
*/
|
||||
export function run_rhai(script) {
|
||||
const ptr0 = passStringToWasm0(script, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.run_rhai(ptr0, len0);
|
||||
if (ret[2]) {
|
||||
throw takeFromExternrefTable0(ret[1]);
|
||||
}
|
||||
return takeFromExternrefTable0(ret[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and unlock a new keyspace with the given name and password
|
||||
* @param {string} keyspace
|
||||
@ -239,11 +266,6 @@ export function lock_session() {
|
||||
wasm.lock_session();
|
||||
}
|
||||
|
||||
function takeFromExternrefTable0(idx) {
|
||||
const value = wasm.__wbindgen_export_2.get(idx);
|
||||
wasm.__externref_table_dealloc(idx);
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* Get metadata of the currently selected keypair
|
||||
* @returns {any}
|
||||
@ -277,6 +299,42 @@ export function is_unlocked() {
|
||||
return ret !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default public key for a workspace (keyspace)
|
||||
* This returns the public key of the first keypair in the keyspace
|
||||
* @param {string} workspace_id
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
export function get_workspace_default_public_key(workspace_id) {
|
||||
const ptr0 = passStringToWasm0(workspace_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.get_workspace_default_public_key(ptr0, len0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current unlocked public key as hex string
|
||||
* @returns {string}
|
||||
*/
|
||||
export function get_current_unlocked_public_key() {
|
||||
let deferred2_0;
|
||||
let deferred2_1;
|
||||
try {
|
||||
const ret = wasm.get_current_unlocked_public_key();
|
||||
var ptr1 = ret[0];
|
||||
var len1 = ret[1];
|
||||
if (ret[3]) {
|
||||
ptr1 = 0; len1 = 0;
|
||||
throw takeFromExternrefTable0(ret[2]);
|
||||
}
|
||||
deferred2_0 = ptr1;
|
||||
deferred2_1 = len1;
|
||||
return getStringFromWasm0(ptr1, len1);
|
||||
} finally {
|
||||
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all keypairs from the current session
|
||||
* Returns an array of keypair objects with id, type, and metadata
|
||||
@ -323,7 +381,7 @@ function passArray8ToWasm0(arg, malloc) {
|
||||
return ptr;
|
||||
}
|
||||
/**
|
||||
* Sign message with current session
|
||||
* Sign message with current session (requires selected keypair)
|
||||
* @param {Uint8Array} message
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
@ -334,6 +392,41 @@ export function sign(message) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current keyspace name
|
||||
* @returns {string}
|
||||
*/
|
||||
export function get_current_keyspace_name() {
|
||||
let deferred2_0;
|
||||
let deferred2_1;
|
||||
try {
|
||||
const ret = wasm.get_current_keyspace_name();
|
||||
var ptr1 = ret[0];
|
||||
var len1 = ret[1];
|
||||
if (ret[3]) {
|
||||
ptr1 = 0; len1 = 0;
|
||||
throw takeFromExternrefTable0(ret[2]);
|
||||
}
|
||||
deferred2_0 = ptr1;
|
||||
deferred2_1 = len1;
|
||||
return getStringFromWasm0(ptr1, len1);
|
||||
} finally {
|
||||
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign message with default keypair (first keypair in keyspace) without changing session state
|
||||
* @param {Uint8Array} message
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
export function sign_with_default_keypair(message) {
|
||||
const ptr0 = passArray8ToWasm0(message, wasm.__wbindgen_malloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.sign_with_default_keypair(ptr0, len0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a signature with the current session's selected keypair
|
||||
* @param {Uint8Array} message
|
||||
@ -373,46 +466,162 @@ export function decrypt_data(encrypted) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the scripting environment (must be called before run_rhai)
|
||||
*/
|
||||
export function init_rhai_env() {
|
||||
wasm.init_rhai_env();
|
||||
function __wbg_adapter_34(arg0, arg1, arg2) {
|
||||
wasm.closure135_externref_shim(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Securely run a Rhai script in the extension context (must be called only after user approval)
|
||||
* @param {string} script
|
||||
* @returns {any}
|
||||
*/
|
||||
export function run_rhai(script) {
|
||||
const ptr0 = passStringToWasm0(script, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.run_rhai(ptr0, len0);
|
||||
if (ret[2]) {
|
||||
throw takeFromExternrefTable0(ret[1]);
|
||||
}
|
||||
return takeFromExternrefTable0(ret[0]);
|
||||
function __wbg_adapter_39(arg0, arg1) {
|
||||
wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha4436a3f79fb1a0f(arg0, arg1);
|
||||
}
|
||||
|
||||
function __wbg_adapter_32(arg0, arg1, arg2) {
|
||||
wasm.closure121_externref_shim(arg0, arg1, arg2);
|
||||
function __wbg_adapter_44(arg0, arg1, arg2) {
|
||||
wasm.closure199_externref_shim(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
function __wbg_adapter_35(arg0, arg1, arg2) {
|
||||
wasm.closure150_externref_shim(arg0, arg1, arg2);
|
||||
function __wbg_adapter_49(arg0, arg1) {
|
||||
wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hf148c54a4a246cea(arg0, arg1);
|
||||
}
|
||||
|
||||
function __wbg_adapter_38(arg0, arg1, arg2) {
|
||||
wasm.closure227_externref_shim(arg0, arg1, arg2);
|
||||
function __wbg_adapter_52(arg0, arg1, arg2) {
|
||||
wasm.closure264_externref_shim(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
function __wbg_adapter_138(arg0, arg1, arg2, arg3) {
|
||||
wasm.closure1879_externref_shim(arg0, arg1, arg2, arg3);
|
||||
function __wbg_adapter_55(arg0, arg1, arg2) {
|
||||
wasm.closure349_externref_shim(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
function __wbg_adapter_195(arg0, arg1, arg2, arg3) {
|
||||
wasm.closure2004_externref_shim(arg0, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
const __wbindgen_enum_BinaryType = ["blob", "arraybuffer"];
|
||||
|
||||
const __wbindgen_enum_IdbTransactionMode = ["readonly", "readwrite", "versionchange", "readwriteflush", "cleanup"];
|
||||
|
||||
const SigSocketConnectionFinalization = (typeof FinalizationRegistry === 'undefined')
|
||||
? { register: () => {}, unregister: () => {} }
|
||||
: new FinalizationRegistry(ptr => wasm.__wbg_sigsocketconnection_free(ptr >>> 0, 1));
|
||||
/**
|
||||
* WASM-bindgen wrapper for SigSocket client
|
||||
*
|
||||
* This provides a clean JavaScript API for the browser extension to:
|
||||
* - Connect to SigSocket servers
|
||||
* - Send responses to sign requests
|
||||
* - Manage connection state
|
||||
*/
|
||||
export class SigSocketConnection {
|
||||
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.__wbg_ptr;
|
||||
this.__wbg_ptr = 0;
|
||||
SigSocketConnectionFinalization.unregister(this);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_sigsocketconnection_free(ptr, 0);
|
||||
}
|
||||
/**
|
||||
* Create a new SigSocket connection
|
||||
*/
|
||||
constructor() {
|
||||
const ret = wasm.sigsocketconnection_new();
|
||||
this.__wbg_ptr = ret >>> 0;
|
||||
SigSocketConnectionFinalization.register(this, this.__wbg_ptr, this);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Connect to a SigSocket server
|
||||
*
|
||||
* # Arguments
|
||||
* * `server_url` - WebSocket server URL (e.g., "ws://localhost:8080/ws")
|
||||
* * `public_key_hex` - Client's public key as hex string
|
||||
*
|
||||
* # Returns
|
||||
* * `Ok(())` - Successfully connected
|
||||
* * `Err(error)` - Connection failed
|
||||
* @param {string} server_url
|
||||
* @param {string} public_key_hex
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
connect(server_url, public_key_hex) {
|
||||
const ptr0 = passStringToWasm0(server_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ptr1 = passStringToWasm0(public_key_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.sigsocketconnection_connect(this.__wbg_ptr, ptr0, len0, ptr1, len1);
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Send a response to a sign request
|
||||
*
|
||||
* This should be called by the extension after the user has approved
|
||||
* a sign request and the message has been signed.
|
||||
*
|
||||
* # Arguments
|
||||
* * `request_id` - ID of the original request
|
||||
* * `message_base64` - Original message (base64-encoded)
|
||||
* * `signature_hex` - Signature as hex string
|
||||
*
|
||||
* # Returns
|
||||
* * `Ok(())` - Response sent successfully
|
||||
* * `Err(error)` - Failed to send response
|
||||
* @param {string} request_id
|
||||
* @param {string} message_base64
|
||||
* @param {string} signature_hex
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
send_response(request_id, message_base64, signature_hex) {
|
||||
const ptr0 = passStringToWasm0(request_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ptr1 = passStringToWasm0(message_base64, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
const ptr2 = passStringToWasm0(signature_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len2 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.sigsocketconnection_send_response(this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2);
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Send a rejection for a sign request
|
||||
*
|
||||
* This should be called when the user rejects a sign request.
|
||||
*
|
||||
* # Arguments
|
||||
* * `request_id` - ID of the request to reject
|
||||
* * `reason` - Reason for rejection (optional)
|
||||
*
|
||||
* # Returns
|
||||
* * `Ok(())` - Rejection sent successfully
|
||||
* * `Err(error)` - Failed to send rejection
|
||||
* @param {string} request_id
|
||||
* @param {string} reason
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
send_rejection(request_id, reason) {
|
||||
const ptr0 = passStringToWasm0(request_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ptr1 = passStringToWasm0(reason, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.sigsocketconnection_send_rejection(this.__wbg_ptr, ptr0, len0, ptr1, len1);
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Disconnect from the SigSocket server
|
||||
*/
|
||||
disconnect() {
|
||||
wasm.sigsocketconnection_disconnect(this.__wbg_ptr);
|
||||
}
|
||||
/**
|
||||
* Check if connected to the server
|
||||
* @returns {boolean}
|
||||
*/
|
||||
is_connected() {
|
||||
const ret = wasm.sigsocketconnection_is_connected(this.__wbg_ptr);
|
||||
return ret !== 0;
|
||||
}
|
||||
}
|
||||
|
||||
async function __wbg_load(module, imports) {
|
||||
if (typeof Response === 'function' && module instanceof Response) {
|
||||
if (typeof WebAssembly.instantiateStreaming === 'function') {
|
||||
@ -467,6 +676,10 @@ function __wbg_get_imports() {
|
||||
const ret = arg0.crypto;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_data_432d9c3df2630942 = function(arg0) {
|
||||
const ret = arg0.data;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_error_524f506f44df1645 = function(arg0) {
|
||||
console.error(arg0);
|
||||
};
|
||||
@ -539,10 +752,23 @@ function __wbg_get_imports() {
|
||||
const ret = result;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_instanceof_Window_def73ea0955fc569 = function(arg0) {
|
||||
let result;
|
||||
try {
|
||||
result = arg0 instanceof Window;
|
||||
} catch (_) {
|
||||
result = false;
|
||||
}
|
||||
const ret = result;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_length_52b6c4580c5ec934 = function(arg0) {
|
||||
const ret = arg0.length;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_log_c222819a41e063d3 = function(arg0) {
|
||||
console.log(arg0);
|
||||
};
|
||||
imports.wbg.__wbg_msCrypto_a61aeb35a24c1329 = function(arg0) {
|
||||
const ret = arg0.msCrypto;
|
||||
return ret;
|
||||
@ -558,7 +784,7 @@ function __wbg_get_imports() {
|
||||
const a = state0.a;
|
||||
state0.a = 0;
|
||||
try {
|
||||
return __wbg_adapter_138(a, state0.b, arg0, arg1);
|
||||
return __wbg_adapter_195(a, state0.b, arg0, arg1);
|
||||
} finally {
|
||||
state0.a = a;
|
||||
}
|
||||
@ -577,6 +803,10 @@ function __wbg_get_imports() {
|
||||
const ret = new Array();
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_new_92c54fc74574ef55 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = new WebSocket(getStringFromWasm0(arg0, arg1));
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_new_a12002a7f91c75be = function(arg0) {
|
||||
const ret = new Uint8Array(arg0);
|
||||
return ret;
|
||||
@ -609,6 +839,12 @@ function __wbg_get_imports() {
|
||||
const ret = arg0.objectStore(getStringFromWasm0(arg1, arg2));
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_onConnectionStateChanged_b0dc098522afadba = function(arg0) {
|
||||
onConnectionStateChanged(arg0 !== 0);
|
||||
};
|
||||
imports.wbg.__wbg_onSignRequestReceived_93232ba7a0919705 = function(arg0, arg1, arg2, arg3) {
|
||||
onSignRequestReceived(getStringFromWasm0(arg0, arg1), getStringFromWasm0(arg2, arg3));
|
||||
};
|
||||
imports.wbg.__wbg_open_88b1390d99a7c691 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = arg0.open(getStringFromWasm0(arg1, arg2));
|
||||
return ret;
|
||||
@ -643,6 +879,10 @@ function __wbg_get_imports() {
|
||||
imports.wbg.__wbg_randomFillSync_ac0988aba3254290 = function() { return handleError(function (arg0, arg1) {
|
||||
arg0.randomFillSync(arg1);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_readyState_7ef6e63c349899ed = function(arg0) {
|
||||
const ret = arg0.readyState;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_require_60cc747a6bc5215a = function() { return handleError(function () {
|
||||
const ret = module.require;
|
||||
return ret;
|
||||
@ -655,12 +895,34 @@ function __wbg_get_imports() {
|
||||
const ret = arg0.result;
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_send_0293179ba074ffb4 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
arg0.send(getStringFromWasm0(arg1, arg2));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_setTimeout_f2fe5af8e3debeb3 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = arg0.setTimeout(arg1, arg2);
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_set_65595bdd868b3009 = function(arg0, arg1, arg2) {
|
||||
arg0.set(arg1, arg2 >>> 0);
|
||||
};
|
||||
imports.wbg.__wbg_setbinaryType_92fa1ffd873b327c = function(arg0, arg1) {
|
||||
arg0.binaryType = __wbindgen_enum_BinaryType[arg1];
|
||||
};
|
||||
imports.wbg.__wbg_setonclose_14fc475a49d488fc = function(arg0, arg1) {
|
||||
arg0.onclose = arg1;
|
||||
};
|
||||
imports.wbg.__wbg_setonerror_8639efe354b947cd = function(arg0, arg1) {
|
||||
arg0.onerror = arg1;
|
||||
};
|
||||
imports.wbg.__wbg_setonerror_d7e3056cc6e56085 = function(arg0, arg1) {
|
||||
arg0.onerror = arg1;
|
||||
};
|
||||
imports.wbg.__wbg_setonmessage_6eccab530a8fb4c7 = function(arg0, arg1) {
|
||||
arg0.onmessage = arg1;
|
||||
};
|
||||
imports.wbg.__wbg_setonopen_2da654e1f39745d5 = function(arg0, arg1) {
|
||||
arg0.onopen = arg1;
|
||||
};
|
||||
imports.wbg.__wbg_setonsuccess_afa464ee777a396d = function(arg0, arg1) {
|
||||
arg0.onsuccess = arg1;
|
||||
};
|
||||
@ -695,6 +957,10 @@ function __wbg_get_imports() {
|
||||
const ret = arg0.then(arg1);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_then_48b406749878a531 = function(arg0, arg1, arg2) {
|
||||
const ret = arg0.then(arg1, arg2);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_transaction_d6d07c3c9963c49e = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = arg0.transaction(arg1, __wbindgen_enum_IdbTransactionMode[arg2]);
|
||||
return ret;
|
||||
@ -703,6 +969,9 @@ function __wbg_get_imports() {
|
||||
const ret = arg0.versions;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_warn_4ca3906c248c47c4 = function(arg0) {
|
||||
console.warn(arg0);
|
||||
};
|
||||
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
||||
const obj = arg0.original;
|
||||
if (obj.cnt-- == 1) {
|
||||
@ -712,16 +981,40 @@ function __wbg_get_imports() {
|
||||
const ret = false;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper378 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 122, __wbg_adapter_32);
|
||||
imports.wbg.__wbindgen_closure_wrapper1181 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 350, __wbg_adapter_55);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper549 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 151, __wbg_adapter_35);
|
||||
imports.wbg.__wbindgen_closure_wrapper335 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 133, __wbg_adapter_34);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper857 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 228, __wbg_adapter_38);
|
||||
imports.wbg.__wbindgen_closure_wrapper336 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 133, __wbg_adapter_34);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper337 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 133, __wbg_adapter_39);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper340 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 133, __wbg_adapter_34);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper657 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 200, __wbg_adapter_44);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper658 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 200, __wbg_adapter_44);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper661 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 200, __wbg_adapter_49);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper876 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 265, __wbg_adapter_52);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
|
||||
@ -778,6 +1071,14 @@ function __wbg_get_imports() {
|
||||
const ret = wasm.memory;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
|
||||
const obj = arg1;
|
||||
const ret = typeof(obj) === 'string' ? obj : undefined;
|
||||
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len1 = WASM_VECTOR_LEN;
|
||||
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
||||
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
||||
};
|
||||
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
|
||||
const ret = getStringFromWasm0(arg0, arg1);
|
||||
return ret;
|
||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user