Skip to content

Commit

Permalink
feat: electrum bitcoin wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
ben-grande committed Jan 31, 2024
1 parent 4b8abde commit e35c9fb
Show file tree
Hide file tree
Showing 13 changed files with 301 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/BOOTSTRAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ you accomplish your mission.
- [mutt](../salt/mutt/README.md)
- [signal](../salt/signal/README.md)

- Electronic cash:
- [electrum](../salt/electrum/README.md)

### Files

- USB:
Expand Down
86 changes: 86 additions & 0 deletions salt/electrum/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# electrum

Electrum Bitcoin Wallet in Qubes OS.

## Table of Contents

* [Description](#description)
* [Installation](#installation)
* [Usage](#usage)
* [Wallet cooperation](#wallet-cooperation)
* [Cold wallet terminology](#cold-wallet-terminology)

## Description

Setup multiple lightweights Electrum Bitcoin Wallets, one offline qube named
"electrum-cold" and one online qube based on Whonix-Workstation named
"electrum-hot".

## Installation

- Top
```sh
qubesctl top.enable electrum
qubesctl --targets=tpl-electrum,electrum-cold,electrum-hot state.apply
qubesctl top.disable electrum
qubesctl state.apply electrum.appmenus
```

- State
<!-- pkg:begin:post-install -->
```sh
qubesctl state.apply electrum.create
qubesctl --skip-dom0 --targets=tpl-electrum state.apply electrum.install
qubesctl --skip-dom0 --targets=electrum-cold,electrum-hot state.apply electrum.configure
qubesctl state.apply electrum.appmenus
```
<!-- pkg:end:post-install -->

## Usage

The qube `electrum-cold` serves as a cold wallet, while the `electrum-hot` is
networked via tor and you can use it as a watching-only (only pub key present)
or hot wallet (private key present).

### Wallet cooperation

If you plan to create private keys on any wallet, it is recommended to pause
or shutdown all qubes to reduce the side-channel attack surface.

As you have both types of wallets, a networked and an offline one, with the
networked wallet you can broadcast transactions while with the offline one,
you sign them. Sharing data between the qubes can be done with `qvm-copy` and
the process of combining a watching-only and a cold wallet is explained in the
[Electrum wiki](https://electrum.readthedocs.io/en/latest/coldstorage.html).

### Cold wallet terminology

I can expect some comments complaining about the term `cold wallet` when
using Qubes OS with an online system. We use this term to refer to an isolated
environment (a qube) that has no internet connection.

You are free to use a non-Qubes physically air-gapped system if you prefer,
you just have to remove the Audio stack (microphone, speakers), Video stack
(camera), USB stack (external ports, Bluetooth), Network stack (network
cards), External reference lights (blinking pattern). If you use a hardware
wallet, you are dependent on a specific hardware vendor and you will need to
choose at least an insecure transfer method, scanning QR code where you can
expose the camera to the data being read, connecting via NFC/USB/SD card
exposes to the USB stack, transfer via radio exposes all devices nearby to the
signal being passed, guard against supply-chain attacks. In the end, your
air-gapped system is not so secure as you thought it to be.

Yes, a Xen exploit that reaches Dom0 or a CPU exploit that can infer
[the memory contents of other running VMs](https://www.qubes-os.org/news/2023/11/14/qsb-096/)
or [the contents of data from a different execution context on the
same CPU core](https://www.qubes-os.org/news/2023/09/27/qsb-094/) can
compromise private private keys, so it is up to you, the user, to choose your
strategy.

Another possibility is a fully offline Qubes OS with this formula installed,
but then again, transferring the data safely to communicate with a networked
device for the transactions to be broadcasted is still a hard thing to fix for
physical air-gapped systems.

There is no consensus on the best solution, choose the option that you can
have more security, not the one you "fell" more secure.
8 changes: 8 additions & 0 deletions salt/electrum/appmenus.sls
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{#
SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. <[email protected]>

SPDX-License-Identifier: AGPL-3.0-or-later
#}

{% from 'utils/macros/sync-appmenus.sls' import sync_appmenus -%}
{{ sync_appmenus('tpl-' ~ sls_path) }}
10 changes: 10 additions & 0 deletions salt/electrum/appmenus.top
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{#
SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. <[email protected]>

SPDX-License-Identifier: AGPL-3.0-or-later
#}

base:
'dom0':
- match: nodegroup
- electrum.appmenus
8 changes: 8 additions & 0 deletions salt/electrum/clone.sls
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{#
SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. <[email protected]>

SPDX-License-Identifier: AGPL-3.0-or-later
#}

{% from 'utils/macros/clone-template.sls' import clone_template -%}
{{ clone_template('debian-minimal', sls_path) }}
10 changes: 10 additions & 0 deletions salt/electrum/clone.top
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{#
SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. <[email protected]>

SPDX-License-Identifier: AGPL-3.0-or-later
#}

base:
'dom0':
- match: nodegroup
- electrum.clone
24 changes: 24 additions & 0 deletions salt/electrum/configure.sls
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{#
SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. <[email protected]>

SPDX-License-Identifier: AGPL-3.0-or-later
#}

{% if grains['nodename'] != 'dom0' -%}

include:
- dev.home-cleanup
- dotfiles.copy-x11
- dotfiles.copy-sh

"{{ slsdotpath }}-setconfig-auto_connect":
cmd.run:
- name: electrum --offline setconfig auto_connect false
- runas: user

"{{ slsdotpath }}-setconfig-check_updates":
cmd.run:
- name: electrum --offline setconfig check_updates false
- runas: user

{% endif -%}
10 changes: 10 additions & 0 deletions salt/electrum/configure.top
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{#
SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. <[email protected]>

SPDX-License-Identifier: AGPL-3.0-or-later
#}

base:
'electrum':
- match: nodegroup
- electrum.configure
82 changes: 82 additions & 0 deletions salt/electrum/create.sls
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{#
SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. <[email protected]>

SPDX-License-Identifier: AGPL-3.0-or-later
#}

{%- from "qvm/template.jinja" import load -%}
{%- import "whonix/template.jinja" as whonix -%}
include:
- .clone
- whonix.create
{% load_yaml as defaults -%}
name: tpl-{{ slsdotpath }}
force: True
require:
- sls: {{ slsdotpath }}.clone
prefs:
- audiovm: ""
features:
- set:
- default-menu-items: "qubes-run-terminal.desktop qubes-start.desktop electrum.desktop"
{%- endload %}
{{ load(defaults) }}
{% load_yaml as defaults -%}
name: {{ slsdotpath }}-cold
force: True
require:
- sls: {{ slsdotpath }}.clone
present:
- template: tpl-{{ slsdotpath }}
- label: gray
prefs:
- template: tpl-{{ slsdotpath }}
- label: gray
- netvm: ""
- audiovm: ""
- vcpus: 1
- memory: 400
- maxmem: 600
- autostart: False
- include_in_backups: True
features:
- disable:
- service.cups
- service.cups-browsed
- set:
- menu-items: "qubes-run-terminal.desktop qubes-start.desktop electrum.desktop"
{%- endload %}
{{ load(defaults) }}
{% load_yaml as defaults -%}
name: {{ slsdotpath }}-hot
force: True
require:
- sls: {{ slsdotpath }}.clone
present:
- template: {{ whonix.whonix_workstation_template }}
- label: orange
prefs:
- template: {{ whonix.whonix_workstation_template }}
- label: orange
- audiovm: ""
- vcpus: 1
- memory: 400
- maxmem: 600
- autostart: False
- include_in_backups: True
tags:
- add:
- anon-vm
features:
- disable:
- service.cups
- service.cups-browsed
- set:
- menu-items: "qubes-run-terminal.desktop qubes-start.desktop qubes-open-file-manager.desktop electrum.desktop"
{%- endload %}
{{ load(defaults) }}
10 changes: 10 additions & 0 deletions salt/electrum/create.top
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{#
SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. <[email protected]>

SPDX-License-Identifier: AGPL-3.0-or-later
#}

base:
'dom0':
- match: nodegroup
- electrum.create
14 changes: 14 additions & 0 deletions salt/electrum/init.top
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{#
SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. <[email protected]>

SPDX-License-Identifier: AGPL-3.0-or-later
#}

base:
'dom0':
- match: nodegroup
- electrum.create
'tpl-electrum':
- electrum.install
'electrum-cold,electrum-hot':
- electrum.configure
27 changes: 27 additions & 0 deletions salt/electrum/install.sls
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{#
SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. <[email protected]>

SPDX-License-Identifier: AGPL-3.0-or-later
#}

{% if grains['nodename'] != 'dom0' -%}

include:
- dev.home-cleanup
- dotfiles.copy-sh
- dotfiles.copy-x11

"{{ slsdotpath }}-updated":
pkg.uptodate:
- refresh: True

"{{ slsdotpath }}-installed":
pkg.installed:
- refresh: True
- install_recommends: False
- skip_suggestions: True
- pkgs:
- electrum
- python3-pyqt5

{% endif -%}
9 changes: 9 additions & 0 deletions salt/electrum/install.top
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{#
SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. <[email protected]>

SPDX-License-Identifier: AGPL-3.0-or-later
#}

base:
'tpl-electrum':
- electrum.install

0 comments on commit e35c9fb

Please sign in to comment.