feat: add ping command
This commit is contained in:
parent
f73f5a90c9
commit
fb60673f67
2 changed files with 153 additions and 46 deletions
|
|
@ -92,7 +92,9 @@ async function serialSigner(path) {
|
||||||
)
|
)
|
||||||
|
|
||||||
this.writer = textEncoder.writable.getWriter()
|
this.writer = textEncoder.writable.getWriter()
|
||||||
this.hwwDhExchange()
|
|
||||||
|
this.hwwPing()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.selectedPort = null
|
this.selectedPort = null
|
||||||
|
|
@ -208,6 +210,9 @@ async function serialSigner(path) {
|
||||||
this.logPublicCommandsResponse(command, commandData)
|
this.logPublicCommandsResponse(command, commandData)
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
|
case COMMAND_PING:
|
||||||
|
this.handlePingResponse(commandData)
|
||||||
|
break
|
||||||
case COMMAND_SIGN_PSBT:
|
case COMMAND_SIGN_PSBT:
|
||||||
this.handleSignResponse(commandData)
|
this.handleSignResponse(commandData)
|
||||||
break
|
break
|
||||||
|
|
@ -261,6 +266,43 @@ async function serialSigner(path) {
|
||||||
this.receivedData += value + '\n'
|
this.receivedData += value + '\n'
|
||||||
const textArea = document.getElementById('serial-port-console')
|
const textArea = document.getElementById('serial-port-console')
|
||||||
if (textArea) textArea.scrollTop = textArea.scrollHeight
|
if (textArea) textArea.scrollTop = textArea.scrollHeight
|
||||||
|
},
|
||||||
|
hwwPing: async function () {
|
||||||
|
try {
|
||||||
|
await this.sendCommandClearText(COMMAND_PING, [window.location.host])
|
||||||
|
} catch (error) {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Failed to ping Hardware Wallet!',
|
||||||
|
caption: `${error}`,
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handlePingResponse: function (res = '') {
|
||||||
|
console.log('### handlePingResponse', res)
|
||||||
|
const [status, deviceId] = res.split(' ')
|
||||||
|
this.deviceId = deviceId
|
||||||
|
|
||||||
|
if (!this.deviceId) {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Missing device ID for Hardware Wallet',
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const device = this.getPairedDevice(deviceId)
|
||||||
|
|
||||||
|
if (device) {
|
||||||
|
this.sharedSecret = nobleSecp256k1.utils.hexToBytes(device.sharedSecretHex)
|
||||||
|
this.hwwCheckSecureConnection()
|
||||||
|
} else {
|
||||||
|
this.hwwDhExchange()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
hwwShowPasswordDialog: async function () {
|
hwwShowPasswordDialog: async function () {
|
||||||
try {
|
try {
|
||||||
|
|
@ -471,6 +513,37 @@ async function serialSigner(path) {
|
||||||
timeout: 10000
|
timeout: 10000
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
hwwCheckSecureConnection: async function () {
|
||||||
|
const testString = 'lnbits'
|
||||||
|
const iv = window.crypto.getRandomValues(new Uint8Array(16))
|
||||||
|
const encrypted = await this.encryptMessage(
|
||||||
|
this.sharedSecret,
|
||||||
|
iv,
|
||||||
|
testString
|
||||||
|
)
|
||||||
|
|
||||||
|
const encryptedHex = nobleSecp256k1.utils.bytesToHex(encrypted)
|
||||||
|
const encryptedIvHex = nobleSecp256k1.utils.bytesToHex(iv)
|
||||||
|
try {
|
||||||
|
await this.writer.write(
|
||||||
|
COMMAND_CHECK_SECURE_CONNECTION +
|
||||||
|
' ' +
|
||||||
|
encryptedHex +
|
||||||
|
encryptedIvHex +
|
||||||
|
'\n'
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Failed to check secure connection!',
|
||||||
|
caption: `${error}`,
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleCheckSecureConnectionResponse: async function(res = '') {
|
||||||
|
console.log('### handleCheckSecureConnectionResponse', res)
|
||||||
|
},
|
||||||
hwwDhExchange: async function () {
|
hwwDhExchange: async function () {
|
||||||
try {
|
try {
|
||||||
this.decryptionKey = nobleSecp256k1.utils.randomPrivateKey()
|
this.decryptionKey = nobleSecp256k1.utils.randomPrivateKey()
|
||||||
|
|
@ -496,6 +569,51 @@ async function serialSigner(path) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleDhExchangeResponse: async function (res = '') {
|
||||||
|
console.log('### handleDhExchangeResponse', res)
|
||||||
|
const [statusCode, data] = res.trim().split(' ')
|
||||||
|
let pubKeyHex, errorMessage, captionMessage
|
||||||
|
switch (statusCode) {
|
||||||
|
case '0':
|
||||||
|
pubKeyHex = data
|
||||||
|
if (!data) errorMessage = 'Failed to exchange DH secret!'
|
||||||
|
break
|
||||||
|
case '1':
|
||||||
|
errorMessage =
|
||||||
|
'Secure connection can only be established in the first 60 seconds after start-up!'
|
||||||
|
captionMessage = 'Restart and try again'
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
errorMessage = 'Unexpected error code'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMessage) {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: errorMessage,
|
||||||
|
caption: captionMessage || '',
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const hwwPublicKey = nobleSecp256k1.Point.fromHex('04' + pubKeyHex)
|
||||||
|
|
||||||
|
this.sharedSecret = nobleSecp256k1
|
||||||
|
.getSharedSecret(this.decryptionKey, hwwPublicKey)
|
||||||
|
.slice(1, 33)
|
||||||
|
|
||||||
|
// window.localStorage.setItem('sharedSecret', nobleSecp256k1.utils.bytesToHex(this.sharedSecret))
|
||||||
|
this.addPairedDevice(this.deviceId, nobleSecp256k1.utils.bytesToHex(this.sharedSecret))
|
||||||
|
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'positive',
|
||||||
|
message: 'Secure session created!',
|
||||||
|
timeout: 5000
|
||||||
|
})
|
||||||
|
},
|
||||||
hwwHelp: async function () {
|
hwwHelp: async function () {
|
||||||
try {
|
try {
|
||||||
await this.sendCommandSecure(COMMAND_HELP)
|
await this.sendCommandSecure(COMMAND_HELP)
|
||||||
|
|
@ -575,47 +693,7 @@ async function serialSigner(path) {
|
||||||
const fingerprint = args[2].trim()
|
const fingerprint = args[2].trim()
|
||||||
this.xpubResolve({xpub, fingerprint})
|
this.xpubResolve({xpub, fingerprint})
|
||||||
},
|
},
|
||||||
handleDhExchangeResponse: async function (res = '') {
|
|
||||||
console.log('### handleDhExchangeResponse', res)
|
|
||||||
const [statusCode, data] = res.trim().split(' ')
|
|
||||||
let pubKeyHex, errorMessage, captionMessage
|
|
||||||
switch (statusCode) {
|
|
||||||
case '0':
|
|
||||||
pubKeyHex = data
|
|
||||||
if(!data) errorMessage = 'Failed to exchange DH secret!'
|
|
||||||
break
|
|
||||||
case '1':
|
|
||||||
errorMessage = 'Secure connection can only be established in the first 60 seconds after start-up!'
|
|
||||||
captionMessage = 'Restart and try again'
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
errorMessage = 'Unexpected error code'
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage) {
|
|
||||||
this.$q.notify({
|
|
||||||
type: 'warning',
|
|
||||||
message: errorMessage,
|
|
||||||
caption: captionMessage || '',
|
|
||||||
timeout: 10000
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const hwwPublicKey = nobleSecp256k1.Point.fromHex('04' + pubKeyHex)
|
|
||||||
|
|
||||||
this.sharedSecret = nobleSecp256k1
|
|
||||||
.getSharedSecret(this.decryptionKey, hwwPublicKey)
|
|
||||||
.slice(1, 33)
|
|
||||||
|
|
||||||
this.$q.notify({
|
|
||||||
type: 'positive',
|
|
||||||
message: 'Secure session created!',
|
|
||||||
timeout: 5000
|
|
||||||
})
|
|
||||||
},
|
|
||||||
hwwShowSeed: async function () {
|
hwwShowSeed: async function () {
|
||||||
try {
|
try {
|
||||||
this.hww.showSeedDialog = true
|
this.hww.showSeedDialog = true
|
||||||
|
|
@ -673,7 +751,6 @@ async function serialSigner(path) {
|
||||||
},
|
},
|
||||||
|
|
||||||
sendCommandSecure: async function (command, attrs = []) {
|
sendCommandSecure: async function (command, attrs = []) {
|
||||||
console.log('### sendCommandSecure')
|
|
||||||
const message = [command].concat(attrs).join(' ')
|
const message = [command].concat(attrs).join(' ')
|
||||||
const iv = window.crypto.getRandomValues(new Uint8Array(16))
|
const iv = window.crypto.getRandomValues(new Uint8Array(16))
|
||||||
const encrypted = await this.encryptMessage(
|
const encrypted = await this.encryptMessage(
|
||||||
|
|
@ -684,9 +761,12 @@ async function serialSigner(path) {
|
||||||
|
|
||||||
const encryptedHex = nobleSecp256k1.utils.bytesToHex(encrypted)
|
const encryptedHex = nobleSecp256k1.utils.bytesToHex(encrypted)
|
||||||
const encryptedIvHex = nobleSecp256k1.utils.bytesToHex(iv)
|
const encryptedIvHex = nobleSecp256k1.utils.bytesToHex(iv)
|
||||||
console.log('### encryptedIvHex', encryptedIvHex)
|
|
||||||
await this.writer.write(encryptedHex + encryptedIvHex + '\n')
|
await this.writer.write(encryptedHex + encryptedIvHex + '\n')
|
||||||
},
|
},
|
||||||
|
sendCommandClearText: async function (command, attrs = []) {
|
||||||
|
const message = [command].concat(attrs).join(' ')
|
||||||
|
await this.writer.write(message + '\n')
|
||||||
|
},
|
||||||
extractCommand: async function (value) {
|
extractCommand: async function (value) {
|
||||||
const command = value.split(' ')[0]
|
const command = value.split(' ')[0]
|
||||||
const commandData = value.substring(command.length).trim()
|
const commandData = value.substring(command.length).trim()
|
||||||
|
|
@ -694,7 +774,8 @@ async function serialSigner(path) {
|
||||||
if (
|
if (
|
||||||
command === COMMAND_DH_EXCHANGE ||
|
command === COMMAND_DH_EXCHANGE ||
|
||||||
command === COMMAND_LOG ||
|
command === COMMAND_LOG ||
|
||||||
command === COMMAND_PASSWORD_CLEAR
|
command === COMMAND_PASSWORD_CLEAR ||
|
||||||
|
command === COMMAND_PING
|
||||||
)
|
)
|
||||||
return {command, commandData}
|
return {command, commandData}
|
||||||
|
|
||||||
|
|
@ -753,10 +834,33 @@ async function serialSigner(path) {
|
||||||
const aesCbc = new aesjs.ModeOfOperation.cbc(key, iv)
|
const aesCbc = new aesjs.ModeOfOperation.cbc(key, iv)
|
||||||
const decryptedBytes = aesCbc.decrypt(encryptedBytes)
|
const decryptedBytes = aesCbc.decrypt(encryptedBytes)
|
||||||
return decryptedBytes
|
return decryptedBytes
|
||||||
|
},
|
||||||
|
getPairedDevices: function() {
|
||||||
|
console.log('### getPairedDevices', window.localStorage.getItem('lnbits-paired-devices'))
|
||||||
|
return JSON.parse(window.localStorage.getItem('lnbits-paired-devices')) || []
|
||||||
|
},
|
||||||
|
getPairedDevice: function(deviceId) {
|
||||||
|
const devices = this.getPairedDevices()
|
||||||
|
return devices.find(d => d.id === deviceId)
|
||||||
|
},
|
||||||
|
removePairedDevice: function(deviceId){
|
||||||
|
const devices = this.getPairedDevices()
|
||||||
|
const deviceIndex = devices.indexOf(d => d.id === deviceId)
|
||||||
|
if (deviceIndex !== -1) {
|
||||||
|
devices.splice(deviceIndex, 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addPairedDevice: function(deviceId, sharedSecretHex){
|
||||||
|
const devices = this.getPairedDevices()
|
||||||
|
devices.push({
|
||||||
|
id: deviceId,
|
||||||
|
sharedSecretHex: sharedSecretHex,
|
||||||
|
pairingDate: new Date().toISOString()
|
||||||
|
})
|
||||||
|
window.localStorage.setItem('lnbits-paired-devices', JSON.stringify(devices))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created: async function () {
|
created: async function () {
|
||||||
console.log('### nobleSecp256k1.utils', nobleSecp256k1.utils)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
const PSBT_BASE64_PREFIX = 'cHNidP8'
|
const PSBT_BASE64_PREFIX = 'cHNidP8'
|
||||||
|
|
||||||
|
const COMMAND_PING = '/ping'
|
||||||
const COMMAND_PASSWORD = '/password'
|
const COMMAND_PASSWORD = '/password'
|
||||||
const COMMAND_PASSWORD_CLEAR = '/password-clear'
|
const COMMAND_PASSWORD_CLEAR = '/password-clear'
|
||||||
const COMMAND_SEND_PSBT = '/psbt'
|
const COMMAND_SEND_PSBT = '/psbt'
|
||||||
|
|
@ -12,6 +14,7 @@ const COMMAND_CANCEL = '/cancel'
|
||||||
const COMMAND_XPUB = '/xpub'
|
const COMMAND_XPUB = '/xpub'
|
||||||
const COMMAND_DH_EXCHANGE = '/dh-exchange'
|
const COMMAND_DH_EXCHANGE = '/dh-exchange'
|
||||||
const COMMAND_LOG = '/log'
|
const COMMAND_LOG = '/log'
|
||||||
|
const COMMAND_CHECK_SECURE_CONNECTION = '/check-connection'
|
||||||
|
|
||||||
const DEFAULT_RECEIVE_GAP_LIMIT = 20
|
const DEFAULT_RECEIVE_GAP_LIMIT = 20
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue