ci: docker_tag=dev-test

**Motivations :**
- Correction du chiffrement : PBKDF2 génère les clés du SDK, pas des clés personnalisées
- WebAuthn chiffre maintenant les clés du SDK générées par PBKDF2
- Ajout de getDeviceFromSDK() pour récupérer les clés du SDK

**Modifications :**
- Remplacement de generateSpendKey/generateScanKey par getDeviceFromSDK()
- WebAuthn chiffre maintenant device.sp_wallet.spend_key et device.sp_wallet.scan_key
- Ajout de la méthode getDeviceFromSDK() pour accéder au SDK

**Pages affectées :**
- src/services/secure-credentials.service.ts
This commit is contained in:
NicolasCantu 2025-10-24 01:57:30 +02:00
parent 3f387ee97f
commit 4a3b23c9d7
3 changed files with 81 additions and 64 deletions

View File

@ -542,8 +542,8 @@ async function handleMainPairing(): Promise<void> {
if (mainStatus) {
mainStatus.innerHTML = '<span style="color: var(--error-color)">❌ Failed to create credentials</span>';
}
return;
}
return;
}
// Now proceed with pairing process
console.log('🚀 Starting pairing process...');

View File

@ -137,13 +137,15 @@ export class SecureCredentialsService {
const publicKey = response.getPublicKey();
const credentialId = credential.id;
// Générer les clés privées réelles (spend/scan) avec PBKDF2
const spendKey = await this.generateSpendKey(password);
const scanKey = await this.generateScanKey(password);
// Récupérer les clés du SDK générées par PBKDF2
const device = await this.getDeviceFromSDK();
if (!device || !device.sp_wallet) {
throw new Error('SDK device not found or wallet not initialized');
}
// Chiffrer les clés privées avec la clé WebAuthn
const encryptedSpendKey = await this.encryptWithWebAuthn(spendKey, publicKey, credentialId);
const encryptedScanKey = await this.encryptWithWebAuthn(scanKey, publicKey, credentialId);
// Chiffrer les clés du SDK avec la clé WebAuthn
const encryptedSpendKey = await this.encryptWithWebAuthn(device.sp_wallet.spend_key, publicKey, credentialId);
const encryptedScanKey = await this.encryptWithWebAuthn(device.sp_wallet.scan_key, publicKey, credentialId);
const credentialData: CredentialData = {
spendKey: encryptedSpendKey, // Clé chiffrée
@ -173,6 +175,21 @@ export class SecureCredentialsService {
}
}
/**
* Récupère le device du SDK pour obtenir les clés générées par PBKDF2
*/
private async getDeviceFromSDK(): Promise<any> {
try {
// Importer le service pour accéder au SDK
const { Services } = await import('./service');
const service = await Services.getInstance();
return service.dumpDeviceFromMemory();
} catch (error) {
console.error('❌ Failed to get device from SDK:', error);
throw error;
}
}
/**
* Génère une clé spend avec PBKDF2
*/

View File

@ -291,7 +291,7 @@ export default class Services {
}
// Memory is sufficient, load WebAssembly
Services.instance = await Services.initializing;
Services.instance = await Services.initializing;
Services.initializing = null;
console.log('✅ Services initialized with WebAssembly');
@ -474,50 +474,50 @@ export default class Services {
* Waits for at least one handshake message before returning.
*/
public async connectAllRelays(): Promise<void> {
const relayUrls = Object.keys(this.relayAddresses);
console.log(`🚀 Connecting to ${relayUrls.length} relays in parallel...`);
const relayUrls = Object.keys(this.relayAddresses);
console.log(`🚀 Connecting to ${relayUrls.length} relays in parallel...`);
// Create the relay ready promise immediately when starting connections
this.getRelayReadyPromise();
// Connect to all relays in parallel
// Connect to all relays in parallel
const connectionPromises = relayUrls.map(async wsurl => {
try {
console.log(`🔗 Connecting to: ${wsurl}`);
await this.addWebsocketConnection(wsurl);
console.log(`✅ Successfully connected to: ${wsurl}`);
return wsurl;
} catch (error) {
console.error(`❌ Failed to connect to ${wsurl}:`, error);
return null;
}
});
try {
console.log(`🔗 Connecting to: ${wsurl}`);
await this.addWebsocketConnection(wsurl);
console.log(`✅ Successfully connected to: ${wsurl}`);
return wsurl;
} catch (error) {
console.error(`❌ Failed to connect to ${wsurl}:`, error);
return null;
}
});
// Wait for all connections to complete (success or failure)
const results = await Promise.allSettled(connectionPromises);
const connectedUrls = results
// Wait for all connections to complete (success or failure)
const results = await Promise.allSettled(connectionPromises);
const connectedUrls = results
.filter(
(result): result is PromiseFulfilledResult<string> =>
result.status === 'fulfilled' && result.value !== null
)
.map(result => result.value);
.map(result => result.value);
console.log(`✅ Connected to ${connectedUrls.length}/${relayUrls.length} relays`);
console.log(`✅ Connected to ${connectedUrls.length}/${relayUrls.length} relays`);
// Wait for at least one handshake message if we have connections
if (connectedUrls.length > 0) {
try {
await this.waitForHandshakeMessage();
console.log(`✅ Handshake received from at least one relay`);
} catch (error) {
// Wait for at least one handshake message if we have connections
if (connectedUrls.length > 0) {
try {
await this.waitForHandshakeMessage();
console.log(`✅ Handshake received from at least one relay`);
} catch (error) {
console.warn(
`⚠️ No handshake received within timeout, but continuing with ${connectedUrls.length} connections`
);
// Continue anyway - we have connections even without handshake
// Continue anyway - we have connections even without handshake
}
} else {
console.warn(`⚠️ No relay connections established`);
}
} else {
console.warn(`⚠️ No relay connections established`);
}
}
private getRelayReadyPromise(): Promise<void> {
@ -574,7 +574,7 @@ export default class Services {
* @returns The SP Address if found, or undefined if not.
*/
public getSpAddress(wsurl: string): string | undefined {
return this.relayAddresses[wsurl];
return this.relayAddresses[wsurl];
}
/**
@ -582,10 +582,10 @@ export default class Services {
* @returns An array of objects containing wsurl and spAddress.
*/
public getAllRelays(): { wsurl: string; spAddress: string }[] {
return Object.entries(this.relayAddresses).map(([wsurl, spAddress]) => ({
wsurl,
spAddress,
}));
return Object.entries(this.relayAddresses).map(([wsurl, spAddress]) => ({
wsurl,
spAddress,
}));
}
/**
@ -593,9 +593,9 @@ export default class Services {
*/
public printAllRelays(): void {
console.log('Current relay addresses:');
for (const [wsurl, spAddress] of Object.entries(this.relayAddresses)) {
console.log(`${wsurl} -> ${spAddress}`);
}
for (const [wsurl, spAddress] of Object.entries(this.relayAddresses)) {
console.log(`${wsurl} -> ${spAddress}`);
}
}
public isPaired(): boolean {
@ -653,7 +653,7 @@ export default class Services {
}
private async getTokensFromFaucet(): Promise<void> {
await this.ensureSufficientAmount();
await this.ensureSufficientAmount();
}
// If we're updating a process, we must call that after update especially if roles are part of it
@ -926,12 +926,12 @@ export default class Services {
roles: Record<string, RoleDefinition>
): Promise<ApiReturn> {
// Attendre que le relai soit prêt avec son spAddress
console.log('⏳ Waiting for relays to be ready...');
console.log('⏳ Waiting for relays to be ready...');
// Update UI status
const { updateCreatorStatus } = await import('../utils/sp-address.utils');
updateCreatorStatus('⏳ Waiting for relays to be ready...');
await this.getRelayReadyPromise();
await this.getRelayReadyPromise();
// Vérifier que nous avons maintenant un spAddress
const relays = this.getAllRelays();
@ -992,11 +992,11 @@ export default class Services {
console.log('🔍 DEBUG: Members array sample:', members.slice(0, 3));
const result = this.sdkClient.create_new_process(
encodedPrivateData,
roles,
encodedPublicData,
relayAddress,
feeRate,
encodedPrivateData,
roles,
encodedPublicData,
relayAddress,
feeRate,
members
);
@ -1171,7 +1171,7 @@ export default class Services {
// Only log as error if it's not a pairing-related issue
if (!(e as Error).message?.includes('Failed to handle decrypted message')) {
console.error(`Parsed cipher with error: ${e}`);
console.error(`Parsed cipher with error: ${e}`);
}
}
// await this.saveCipherTxToDb(parsedTx)
@ -1307,7 +1307,7 @@ export default class Services {
}
} catch (scanError) {
console.error('❌ Failed to scan blocks:', scanError);
}
}
}
} catch (e) {
console.debug(e);
@ -1515,7 +1515,7 @@ export default class Services {
// Check if the commitment is set and not null/empty
if (
device.pairing_process_commitment &&
device.pairing_process_commitment !== null &&
device.pairing_process_commitment !== null &&
device.pairing_process_commitment !== ''
) {
console.log('✅ Pairing process commitment found:', device.pairing_process_commitment);
@ -1524,11 +1524,11 @@ export default class Services {
// For quorum=1.0 processes, the creator must commit themselves
// Check if the process is ready for the creator to commit
if (currentPairingId && currentPairingId === processId) {
if (currentPairingId && currentPairingId === processId) {
console.log(
'✅ Creator process is synchronized and ready for self-commitment (quorum=1.0)'
);
return;
return;
}
// For quorum=1 test, if we have a process but no commitment yet,
@ -1577,11 +1577,11 @@ export default class Services {
);
}
}
} catch (e) {
} catch (e) {
console.log(
`❌ Attempt ${i + 1}/${maxRetries}: Error during synchronization - ${(e as Error).message}`
);
}
}
if (i < maxRetries - 1) {
console.log(`⏳ Waiting ${retryDelay}ms before next attempt...`);
@ -1682,7 +1682,7 @@ export default class Services {
throw new Error('SDK not initialized - cannot get amount');
}
try {
const amount = this.sdkClient.get_available_amount();
const amount = this.sdkClient.get_available_amount();
console.log(`💰 SDK get_available_amount() returned: ${amount}`);
// Additional debugging: check wallet state
@ -1701,7 +1701,7 @@ export default class Services {
console.warn('⚠️ Error getting wallet debugging info:', error);
}
return amount;
return amount;
} catch (error) {
console.error('❌ Error calling get_available_amount():', error);
throw error;
@ -2002,7 +2002,7 @@ export default class Services {
throw new Error('Birthday not found');
}
if (birthday === 0) {
if (birthday === 0) {
// This is a new device, set birthday to scan from much earlier to catch faucet transactions
// Scan from 100 blocks earlier to ensure we catch all faucet transactions
console.log('🔧 Updating birthday for new device:', {
@ -2668,7 +2668,7 @@ export default class Services {
*/
public getAllMembersSorted(): Record<string, Member> {
return Object.fromEntries(
Object.entries(this.membersList).sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
Object.entries(this.membersList).sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
);
}