Skip to content

Latest commit

 

History

History
530 lines (378 loc) · 11.4 KB

deck.mdx

File metadata and controls

530 lines (378 loc) · 11.4 KB

import { Graphviz } from 'graphviz-react' import QRCode from 'qrcode.react'

import { Image, Appear, Invert } from 'mdx-deck' import Button from "react-bootstrap/Button";

import customTheme from './theme' export const theme = customTheme

Hardware Wallets

and the Web



#BTC2019

Jon KeepKey


<Image size='cover' style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', }} src="https://user-images.githubusercontent.com/35975617/63715451-00799000-c801-11e9-888b-1281574186db.png">

Follow Along

bit.ly/2L23QaF


What to expect

  • Lesson
  • Hands-on Exercises
  • Follow along

Slides

$ git clone github.com/keepkeyjon/teach-hdwallet
$ cd teach-hdwallet
$ yarn
$ yarn start

⬅️ ➡️ arrow keys to navigate

option-p for presenter mode, hidden notes.


Device Update

Hands-on exercises need v6.1.0+ firmware:


Problem

<Graphviz dot={digraph D { "dApp A" -> "Ledger"; "dApp C" -> "Ledger"; "dApp B" -> "Ledger"; "dApp B" -> "Trezor"; "dApp C" -> "Trezor"; "dApp D" -> "KeepKey"; }} options={{ height: 1000, width: 1000 }} />

  • Every vendor has their own libraries
  • Fragmented wallet support

Solution

<Graphviz dot={digraph D { "dApp A" -> "Common API"; "dApp B" -> "Common API"; "dApp C" -> "Common API"; "Common API" -> "Ledger"; "Common API" -> "Trezor"; "Common API" -> "KeepKey"; }} options={{ height: 1000, width: 1000 }} />

  • Unified api

Solution

github.com/shapeshift/hdwallet


Architecture

<Graphviz dot={digraph D { rankdir="LR"; hdwalletcore [label="hdwallet-core", shape=box]; subgraph cluster_1 { label = "KeepKey"; hdwalletkeepkey [label="hdwallet-keepkey", shape=box]; hdwalletkeepkeywebusb [label="hdwallet-keepkey-webusb", shape=box, fillcolor="aquamarine", style=filled]; hdwalletkeepkeywebusb -> hdwalletkeepkey; hdwalletkeepkeynodewebusb [label="hdwallet-keepkey-nodewebusb", shape=box, fillcolor="darkseagreen1", style=filled]; hdwalletkeepkeynodewebusb -> hdwalletkeepkey; hdwalletkeepkeychromeusb [label="hdwallet-keepkey-chromeusb", shape=box, fillcolor="bisque", style=filled]; hdwalletkeepkeychromeusb -> hdwalletkeepkey; } hdwalletkeepkey -> hdwalletcore; subgraph cluster_2 { label = "Trezor" hdwallettrezor [label="hdwallet-trezor", shape=box]; hdwallettrezorconnect [label="hdwallet-trezor-connect", shape=box, fillcolor="aquamarine", style=filled]; hdwallettrezorconnect -> hdwallettrezor; } hdwallettrezor -> hdwalletcore; subgraph cluster_3 { label = "Ledger"; hdwalletledger [label="hdwallet-ledger", shape=box]; hdwalletledgerwebusb [label="hdwallet-ledger-webusb", shape=box, fillcolor="aquamarine", style=filled]; hdwalletledgerwebusb -> hdwalletledger; hdwalletledgeru2f [label="hdwallet-ledger-u2f", shape=box, fillcolor="aquamarine", style=filled]; hdwalletledgeru2f -> hdwalletledger; } hdwalletledger -> hdwalletcore; }} options={{ height: 1000, width: 1000 }} />

<Graphviz dot={digraph D { rankdir="LR"; subgraph cluster_0 { label = "Legend"; package [label="package", shape=box]; webpackage [label="web package", shape=box, fillcolor="aquamarine", style=filled]; nodepackage [label="node package", shape=box, fillcolor="darkseagreen1", style=filled]; chromepackage [label="chrome package", shape=box, fillcolor="bisque", style=filled]; } }} options={{ height: 1000, width: 1000 }} />


Exercise 1: Pair Device

<iframe src="https://codesandbox.io/embed/github/keepkeyjon/teach-hdwallet/tree/master/exercises/01-pair-device?autoresize=1&expanddevtools=1&fontsize=14&module=%2Fsrc%2FWallet.tsx" title="Pair Device" allow="usb" style="width:100%; height:850px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"> </iframe>
  • Users should try pairing a KeepKey, or a Trezor
  • May run into issues with
    • Old Firmware
    • Old Trezor Bridge versions
    • Browsers without WebUSB support

Lesson: Feature Tests

  • Coin support varies
    • Devices
    • Firmware Versions
  • Support common subset

Lesson: Feature Tests

// ETH / ETC / Ropsten / Kovan / etc.
if (supportsETH(wallet)) {
  addr = await wallet.ethGetAddress({ ... })
}

// Bitcoin / Litecoin / Dash / Doge / etc.
if (supportsBTC(wallet)) {
  addr = await wallet.btcGetAddress({ ... })
}

Lesson: BIP32 Paths

  • Standard for derivation paths in HD Wallets
  • Notation
    • m/44'/0'/0'/0/0
    • [0x8000002c, 0x80000000, 0x80000000, 0, 0]
  • BIP44 / BIP49 / BIP84

Lesson: BIP32 Paths

  • BIP44
    • m/44'/0'/0'/0/0
    • m/purpose'/coin'/account'/change/index

Lesson: BIP32 Paths

  • Problem
    • No clear standard for ETH
    • Every wallet does it differently 😭

Vendor Path
KeepKey m/44'/60'/x'/0/0
Trezor m/44'/60'/0'/0/x
Ledger m/44'/60'/0'/x
Ledger m/44'/60'/x'/0/0
Coinomi m/44'/60'/x'/0

Lesson: BIP32 Paths

Solution: Ask HDWallet!

wallet.ethGetAccountPaths({
  coin: 'Ethereum',
  accountIdx: 1
})

Lesson: Show an Address

['🐓','🥚'].sort()

let addressNList =
  bip32ToAddressNList(`m/44'/60'/0'/0/0`)
let address = await wallet.ethGetAddress({
  coin: 'Ethereum',
  addressNList,
  showDisplay: true
  address: ???
})

TrezorConnect api requires that we provide the address that we are expecting that the user see when it is shown on their device.

This prevents surprises, but it also means we need to have the address before we can show it on the display.


Lesson: Show an Address

Solution: Ask twice!

let addressNList =
  bip32ToAddressNList(`m/44'/60'/0'/0/0`)

address = await wallet.ethGetAddress({
  coin: 'Ethereum', addressNList,
  showDisplay: false, ...msg })

renderQRCode(address)

address = await wallet.ethGetAddress({
  coin: 'Ethereum', addressNList,
  showDisplay: true, address, ...msg })

Exercise 2: Show Address

<iframe src="https://codesandbox.io/embed/github/keepkeyjon/teach-hdwallet/tree/master/exercises/02-show-address?autoresize=1&expanddevtools=1&fontsize=14&module=%2Fsrc%2FWallet.tsx" title="Show Address" allow="usb" style="width:100%; height:850px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"> </iframe>

docs: 1 2


Lesson: ETH ABI

  • <span style={{ color: '#f0c808' }}>transfer(<span style={{ color: '#1ea4da' }}>address,<span style={{ color: '#dd1c1a' }}>uint256)
  • <span style={{ fontSize: 16, color: 'white' }}>0x<span style={{ fontSize: 16, color: '#f0c808' }}>a9059cbb<span style={{ fontSize: 16, color: '#1ea4da' }}>000000000000000000000073d0385F4d8E00C5e6504C6030F47BF6212736A8<span style={{ fontSize: 16, color: '#dd1c1a' }}>00000000000000000000000000000000000000000000487A9A304539440000

Lesson: ETH Tx Signing

let sig = await wallet.ethSignTx({
  addressNList,
  nonce: '0x01',
  gasPrice: "0x1dcd65000",
  gasLimit: "0x5622",
  value: '0x',
  to: '0xc770EEfAd204B5180dF6a14Ee197D99d808ee52d',
  chainId: 1,
  data: '0xa9059cbb' +
    '000000000000000000000000' +
    '73d0385F4d8E00C5e6504C6030F47BF6212736A8' +
    '0000000000000000000000000000000000000000000000' +
    '487A9A304539440000'
})

Exercise 3: Sign ERC20 Tx

🔫 🐠 🛢️

<iframe src="https://codesandbox.io/embed/github/keepkeyjon/teach-hdwallet/tree/master/exercises/03-sign-erc20?autoresize=1&expanddevtools=1&fontsize=14&module=%2Fsrc%2FWallet.tsx" title="Show Address" allow="usb" style="width:100%; height:850px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"> </iframe>

Lesson: Message Signing

signDigest(hash(message), key)
vs
signDigest(hash("\x19Ethereum Signed Message:\n" + message), key)

"Personal" message signing gets prefixed with a string to distinguish it from transaction signing. This helps protect users from signing malicious payloads that take all their funds, or other funny business.


Lesson: Message Sign & Verify

let res = await wallet.ethSignMessage({
  addressNList: bip32ToAddressNList("m/44'/60'/0'/0/0"),
  message: 'Hello World'
})
let res = await wallet.ethVerifyMessage({
  address: '0x3f2329C9ADFbcCd9A84f52c906E936A42dA18CB8',
  message: 'Hello World',
  signature: '0x29f7212ecc1c76cea81174af267b67506' +
               'f754ea8c73f144afa900a0d85b24b2131' +
               '9621aeb062903e856352f383057101908' +
               '69c3ce5a1425d65ef4fa558d0fc251b'
})

KeepKey doesn't have font support for emojis, so you won't be able to see them when you sign them (you'll see a sadface instead)... But plain old ASCII will work, up to a size limit.


Lesson: UTXO Receive Address

if (!supportsBTC(wallet))
  return

if (!wallet.btcSupportsCoin('Litecoin'))
  return

// Same "Ask Twice" pattern as on ETH
let address = await wallet.btcGetAddress({
  addressNList,
  coin: 'Litecoin',
  showDisplay: false,
  scriptType: BTCInputScriptType.SpendWitness, // p2wpkh, bech32
})

Alternatively, fetch the account's xpub using wallet.getPublicKeys(...), and derive the address yourself, then make the request with showDisplay: true.


Lesson: UTXO Signing

  • Much more involved than for ETH signing
  • No UTXO selection in HDWallet
  • Need to provide full prevTx for legacy UTXOs
    • Allows devices to verify amounts add up

Exercise: Take Home

  • Ideas
    1. Add Ledger support
    2. Add UI for BIP39 Passphrases
    3. Add Sign & Verify message support

Fin