From 58ab68b876aa16694371382ae15e8c99c61efe27 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Fri, 12 Aug 2022 15:12:03 +0300 Subject: [PATCH] feat: use `aes` lib foe encryption --- .../components/serial-signer/serial-signer.js | 91 ++++++++----------- .../watchonly/static/js/crypto/aes.js | 2 +- .../extensions/watchonly/static/js/utils.js | 31 +++++++ 3 files changed, 72 insertions(+), 52 deletions(-) diff --git a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js b/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js index 8dadb194..947a9cc7 100644 --- a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js +++ b/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js @@ -66,12 +66,13 @@ async function serialSigner(path) { } try { - navigator.serial.addEventListener('connect', event => { - console.log('### navigator.serial event: connected!', event) + this.selectedPort = await navigator.serial.requestPort() + this.selectedPort.addEventListener('connect', event => { + console.log('### this.selectedPort event: connected!', event) }) - navigator.serial.addEventListener('disconnect', () => { - console.log('### navigator.serial event: disconnected!', event) + this.selectedPort.addEventListener('disconnect', () => { + console.log('### this.selectedPort event: disconnected!', event) this.selectedPort = null this.hww.authenticated = false this.$q.notify({ @@ -80,7 +81,7 @@ async function serialSigner(path) { timeout: 10000 }) }) - this.selectedPort = await navigator.serial.requestPort() + // Wait for the serial port to open. await this.selectedPort.open(config) this.startSerialPortReading() @@ -91,6 +92,7 @@ async function serialSigner(path) { ) this.writer = textEncoder.writable.getWriter() + this.hwwDhExchange() return true } catch (error) { this.selectedPort = null @@ -454,56 +456,32 @@ async function serialSigner(path) { timeout: 10000 }) }, - hwwHelp: async function () { - // const sharedSecret = - // 'f96c85875055a5586688fea4cf7c4a2bd9541ffcf34f9d663d97e0cf2f6af4af' - // const sharedSecretBytes = hexToBytes(sharedSecret) - // // console.log('### sharedSecret', sharedSecret) - // const key = await window.crypto.subtle.importKey( - // 'raw', - // sharedSecretBytes, - // { - // name: 'AES-CBC', - // length: 256 - // }, - // true, - // ['encrypt', 'decrypt'] - // ) - // d2b9e5e3ff8945236455424e9e25590b8264f13c7484862cca4f5b7b8bf8f1686d218b4f1aacdc27a1df71fa4b530adfd6c8cae6bd926d3f8be8ff55ee4358d1a32569e9f5263ffae7d0eaf413788498 - // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - // 6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710 - // 8f13a7763f021d7701f4100631f6c3d80576fcd0e3718b2594ceb7b910ceed29a334d1019dd6f0ffdba5b6be8c11637d6124d7adbd29c88af13800cb1f980f7d - - // const message = - // 'TextMustBe16ByteTextMustBe16ByteTextMustBe16ByteTextMustBe16Byte' - // const encoded = asciiToUint8Array(message) - // const encrypted = await encryptMessage(key, encoded) - // const encryptedHex = bytesToHex(encrypted) - // console.log('### encrypted hex: ', encryptedHex) - - // const encryptedHex2 = await encryptMessage2(sharedSecretBytes, message) - // console.log('### encryptedHex2', encryptedHex2) - - // const decrypted = await decryptMessage(key, encrypted) - // console.log( - // '### decrypted hex: ', - // bytesToHex(new Uint8Array(decrypted)) - // ) - // console.log( - // '### decrypted ascii: ', - // new TextDecoder().decode(new Uint8Array(decrypted)) - // ) - + hwwDhExchange: async function () { try { this.decryptionKey = nobleSecp256k1.utils.randomPrivateKey() const publicKey = nobleSecp256k1.Point.fromPrivateKey( this.decryptionKey ) const publicKeyHex = publicKey.toHex().slice(2) - console.log('### publicKeyHex:', publicKeyHex) await this.writer.write( COMMAND_DH_EXCHANGE + ' ' + publicKeyHex + '\n' ) + this.$q.notify({ + type: 'positive', + message: 'Starting secure session!', + timeout: 5000 + }) + } catch (error) { + this.$q.notify({ + type: 'warning', + message: 'Failed to send DH Public Key to device!', + caption: `${error}`, + timeout: 10000 + }) + } + }, + hwwHelp: async function () { + try { this.$q.notify({ type: 'positive', message: 'Check display or console for details!', @@ -587,7 +565,7 @@ async function serialSigner(path) { }, handleDhExchangeResponse: async function (res = '') { console.log('### handleDhExchangeResponse', res) - const [pubKeyHex, fingerprint] = res.trim().split(' ') + const [pubKeyHex] = res.trim().split(' ') if (!pubKeyHex) { this.$q.notify({ type: 'warning', @@ -618,6 +596,11 @@ async function serialSigner(path) { true, ['encrypt', 'decrypt'] ) + this.$q.notify({ + type: 'positive', + message: 'Secure session created!', + timeout: 5000 + }) }, hwwShowSeed: async function () { try { @@ -685,8 +668,10 @@ async function serialSigner(path) { sendCommandSecure: async function (command, attrs = []) { const message = [command].concat(attrs).join(' ') - const encodedMessage = asciiToUint8Array(message.length + ' ' + message) - const encrypted = await encryptMessage(this.dheKey, encodedMessage) + const encrypted = await encryptMessage2( + this.sharedSecret, + message.length + ' ' + message + ) const encryptedHex = nobleSecp256k1.utils.bytesToHex(encrypted) await this.writer.write(encryptedHex + '\n') @@ -700,7 +685,9 @@ async function serialSigner(path) { const decryptedValue = await this.decryptData(value) const decryptedCommand = decryptedValue.split(' ')[0] - const decryptedCommandData = decryptedValue.substring(decryptedCommand.length).trim() + const decryptedCommandData = decryptedValue + .substring(decryptedCommand.length) + .trim() return { command: decryptedCommand, commandData: decryptedCommandData @@ -723,7 +710,9 @@ async function serialSigner(path) { ) const data = new TextDecoder().decode(decrypted1) const [len] = data.split(' ') - const command = data.substring(len.length +1, +len + len.length + 1).trim() + const command = data + .substring(len.length + 1, +len + len.length + 1) + .trim() console.log('### decryptData ', data, command) return command } catch (error) { diff --git a/lnbits/extensions/watchonly/static/js/crypto/aes.js b/lnbits/extensions/watchonly/static/js/crypto/aes.js index 2f6ef760..8a28b655 100644 --- a/lnbits/extensions/watchonly/static/js/crypto/aes.js +++ b/lnbits/extensions/watchonly/static/js/crypto/aes.js @@ -47,7 +47,7 @@ if (checkInt(arg.length) && checkInts(arg)) { return new Uint8Array(arg); } - + console.log('### aes encypt') throw new Error('unsupported array-like object'); } diff --git a/lnbits/extensions/watchonly/static/js/utils.js b/lnbits/extensions/watchonly/static/js/utils.js index d6c5393d..36a2247c 100644 --- a/lnbits/extensions/watchonly/static/js/utils.js +++ b/lnbits/extensions/watchonly/static/js/utils.js @@ -229,6 +229,37 @@ async function encryptMessage(key, message) { return new Uint8Array(ciphertext) } +async function encryptMessage2(key, message) { + // The iv must never be reused with a given key. + // iv = window.crypto.getRandomValues(new Uint8Array(16)); + while (message.length % 16 !== 0) message += ' ' + const encodedMessage = asciiToUint8Array(message) + iv = new Uint8Array([ + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0a, + 0x0b, + 0x0c, + 0x0d, + 0x0e, + 0x0f + ]) + + const aesCbc = new aesjs.ModeOfOperation.cbc(key, iv) + const encryptedBytes = aesCbc.encrypt(encodedMessage) + + console.log('### #####################################') + return encryptedBytes +} + async function decryptMessage(key, ciphertext) { iv = new Uint8Array([ 0x00,