feat(mobile): bind device identities and bound voice usage in 0.1.4 dev
This commit is contained in:
1 parent
ac6687181e
commit
5f75dcc99a
24 files changed
+909
-47
No files matched your search
@@ -25,8 +25,74 @@ try {
|
||||
assert.ok(!blob.includes(Buffer.from('local-fake-account-for-encryption-test')))
|
||||
const vault = new MobileVault(join(directory, 'mobile'), safeStorage)
|
||||
assert.equal((await vault.load()).accountToken, 'local-fake-account-for-encryption-test')
|
||||
await vault.save({ ...await vault.load(), deviceId: '11111111-1111-4111-8111-111111111111' })
|
||||
const fingerprint = await window.webContents.executeJavaScript(`(async () => {
|
||||
const { createDeviceIdentity } = await import('./mobile/peer-identity.mjs');
|
||||
const setup = await mobileHost.call('setup');
|
||||
const identity = await createDeviceIdentity(setup.deviceId);
|
||||
await mobileHost.call('identity.save', {deviceId:setup.deviceId, fingerprint:identity.fingerprint});
|
||||
let rejected = false;
|
||||
try { await mobileHost.call('identity.save', {deviceId:setup.deviceId, fingerprint:Array(32).fill('00').join(':')}); }
|
||||
catch { rejected = true; }
|
||||
if (!rejected) throw new Error('Changed identity silently accepted');
|
||||
return identity.fingerprint;
|
||||
})()`)
|
||||
assert.equal((await vault.load()).localFingerprint, fingerprint)
|
||||
const reloaded = new Promise(resolve => window.webContents.once('did-finish-load', resolve))
|
||||
window.webContents.reload(); await reloaded
|
||||
const restored = await window.webContents.executeJavaScript(`(async () => {
|
||||
const { loadDeviceIdentity } = await import('./mobile/peer-identity.mjs');
|
||||
const setup = await mobileHost.call('setup');
|
||||
const identity = await loadDeviceIdentity(setup.deviceId, setup.localFingerprint);
|
||||
return identity.fingerprint;
|
||||
})()`)
|
||||
assert.equal(restored, fingerprint)
|
||||
const originalFetch = globalThis.fetch, phoneId = '33333333-3333-4333-8333-333333333333'
|
||||
const pairingId = '44444444-4444-4444-8444-444444444444', phonePin = Array(32).fill('AB').join(':')
|
||||
let advertisedPin = phonePin, approvals = 0
|
||||
globalThis.fetch = async (url, options) => {
|
||||
const path = new URL(url).pathname
|
||||
if (path === `/v1/aiturk/pairings/${pairingId}/approve` && options.method === 'POST') {
|
||||
approvals++
|
||||
return Response.json({ device_id: phoneId, status: 'approved' })
|
||||
}
|
||||
if (path === '/v1/aiturk/mobile/channels') return Response.json({ channels: [{ id: 'fixture-channel',
|
||||
phone_id: phoneId, sdp: `v=0\r\na=fingerprint:sha-256 ${advertisedPin}\r\n` }] })
|
||||
throw new Error('Unexpected network request in isolated test')
|
||||
}
|
||||
try {
|
||||
const proof = JSON.stringify({ kind: 'aiturk-phone-identity', version: 1, pairingId, deviceId: phoneId, fingerprint: phonePin })
|
||||
const qrImport = await window.webContents.executeJavaScript(`(async () => {
|
||||
const { drawQr } = await import('./mobile/qr.mjs');
|
||||
const canvas = document.createElement('canvas'); drawQr(canvas, ${JSON.stringify(proof)});
|
||||
const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
|
||||
const transfer = new DataTransfer(); transfer.items.add(new File([blob], 'phone-identity.png', {type:'image/png'}));
|
||||
const input = document.getElementById('phone-identity-file'); input.files = transfer.files;
|
||||
await input.onchange({target:input});
|
||||
return {pairingId:document.getElementById('pairing').value, proof:document.getElementById('phone-identity').value,
|
||||
status:document.getElementById('status').textContent};
|
||||
})()`)
|
||||
assert.equal(qrImport.pairingId, pairingId)
|
||||
assert.equal(qrImport.proof, proof)
|
||||
assert.match(qrImport.status, /henüz izin verilmedi/)
|
||||
assert.equal(approvals, 0)
|
||||
const accepted = await window.webContents.executeJavaScript(`mobileHost.call('phone.approve', ${JSON.stringify({ pairingId, code: 'QRSTUVWX', identity: proof })})`)
|
||||
assert.equal(accepted.device_id, phoneId)
|
||||
assert.equal((await vault.load()).phoneFingerprints[phoneId], phonePin)
|
||||
const inbox = await window.webContents.executeJavaScript(`mobileHost.call('inbox')`)
|
||||
assert.equal(inbox.channels[0].trustedPhoneFingerprint, phonePin)
|
||||
advertisedPin = Array(32).fill('CD').join(':')
|
||||
await assert.rejects(window.webContents.executeJavaScript(`mobileHost.call('inbox')`), /kimliği/)
|
||||
await assert.rejects(window.webContents.executeJavaScript(`mobileHost.call('channel.answer', {channelId:'fixture-channel',sdp:'unused'})`), /kimliği/)
|
||||
const changed = JSON.stringify({ kind: 'aiturk-phone-identity', version: 1, pairingId, deviceId: phoneId, fingerprint: advertisedPin })
|
||||
await assert.rejects(window.webContents.executeJavaScript(`mobileHost.call('phone.approve', ${JSON.stringify({ pairingId, code: 'QRSTUVWX', identity: changed })})`), /kimliği/)
|
||||
assert.equal(approvals, 1)
|
||||
assert.equal((await vault.load()).phoneFingerprints[phoneId], phonePin)
|
||||
} finally { globalThis.fetch = originalFetch }
|
||||
await vault.save({})
|
||||
const receipt = { realElectronIpc: 'passed', windowsSafeStorage: 'passed', heading: result.heading, checkedAt: new Date().toISOString() }
|
||||
const receipt = { realElectronIpc: 'passed', windowsSafeStorage: 'passed', desktopIdentityReload: 'passed',
|
||||
silentIdentityReplacement: 'rejected', locallyApprovedPhonePin: 'passed',
|
||||
changedPhoneBeforeAnswer: 'rejected', identityQrImportWithoutApproval: 'passed', heading: result.heading, checkedAt: new Date().toISOString() }
|
||||
await writeFile('apps/mobile/test-results/electron-smoke.json', JSON.stringify(receipt))
|
||||
console.log(JSON.stringify(receipt))
|
||||
window.destroy(); app.exit(0)
|
||||
|
||||
Reference in new issue
Block a user