From cb8c76db0f8523e4265738df0f079555e4b530c3 Mon Sep 17 00:00:00 2001 From: Profpatsch Date: Fri, 5 Mar 2021 23:53:13 +0100 Subject: pkgs/profpatsch: add gpg-private-offline-key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are the scripts I used to print my gpg private key onte a bunch of A4 papers, as QR codes of the paperkey output. It also contains an example of how to automatically read it back in with a ScanSnap „Einzugsscanner“. I think there also was a test that checks that the full roundtrip works :) The QR codes generation was designed in a way that they contain the highest amount of data when printed on A4 paper, while still being high-redundancy enough that you can destroy about 1/4th of them before they become unreadable. The key was also printed as plain paperkey format, so in the worst case when I don’t have a scanner I can type it in by hand. --- pkgs/profpatsch/default.nix | 2 + pkgs/profpatsch/gpg-private-offline-key/: | 77 ++++++++++++++++++++++ .../profpatsch/gpg-private-offline-key/default.nix | 57 ++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 pkgs/profpatsch/gpg-private-offline-key/: create mode 100644 pkgs/profpatsch/gpg-private-offline-key/default.nix (limited to 'pkgs/profpatsch') diff --git a/pkgs/profpatsch/default.nix b/pkgs/profpatsch/default.nix index 0d2a593a..2ff12510 100644 --- a/pkgs/profpatsch/default.nix +++ b/pkgs/profpatsch/default.nix @@ -271,4 +271,6 @@ in rec { backup = import ./backup { inherit pkgs writeExecline getBins backtick; }; + gpg-private-offline-key = import ./gpg-private-offline-key { inherit pkgs writeExecline getBins; }; + } diff --git a/pkgs/profpatsch/gpg-private-offline-key/: b/pkgs/profpatsch/gpg-private-offline-key/: new file mode 100644 index 00000000..1264627f --- /dev/null +++ b/pkgs/profpatsch/gpg-private-offline-key/: @@ -0,0 +1,77 @@ +{ pkgs, writeExecline, getBins }: + +let +# split + + bins = getBins pkgs.coreutils [ "split" "mktemp" "rm" "rmdir" ] + // getBins pkgs.lr [ "lr" ] + // getBins pkgs.xe [ "xe" ] + // getBins pkgs.qrencode [ "qrencode" ]; + + qr-code-props = { + # second highest redundancy level + level = "Q"; + # max amount of bytes that level Q can encode + bytes = "1700"; + }; + + # Takes a private GPG key encoded with paperkey on stdin, + # splits it into pieces into an empty directory that + # has to be provided via PRIVKEY_TMPDIR, + # and execs into the block provided as first argument + # for each qr code image (via stdin). + print-qr-codes = writeExecline "print-qr-codes" {} [ + "importas" "-ui" "privkey_dir" "PRIVKEY_TMPDIR" + "cd" "$privkey_dir" + "if" [ + bins.split + "--bytes=${qr-code-props.bytes}" + # paperkey-encoded key comes from stdin + "-" + ] + "pipeline" [ bins.lr "-0" ] + "forstdin" "-0" "piece" + "importas" "-iu" "piece" "piece" + "redirfd" "-r" "0" "$piece" + "pipeline" [ + bins.qrencode + "--level=${qr-code-props.level}" + "--dpi=300" + "-o-" + ] + "$@" + ]; + + print-qr-codes2 = pkgs.writers.writePython "print-qr-codes" {} '' + import sys + + while True: + str = sys.stdin.bytes.read(${qr-code-props.bytes}) + if str == "": + break + sys.execvl( bins.qrencode + + ''; + + rm-files-in-directory = writeExecline "rm-files-in-directory" { readNArgs = 1; } [ + "if" [ + "pipeline" [ bins.lr "-0" "-t" "depth==1" "$1" ] + bins.xe "-0" bins.rm + ] + bins.rmdir "$1" + ]; + + test = writeExecline "test12" {} [ + "backtick" "-ni" "PRIVKEY_TMPDIR" [ bins.mktemp "-d" ] + "importas" "-i" "privdir" "PRIVKEY_TMPDIR" + "foreground" [ print-qr-codes [ "feh" "-" ] ] + rm-files-in-directory "$privdir" + ]; + + +# scanning: +# for i in (seq 0 9); nix-shell -p zbar --run 'scanimage --device=\'fujitsu:ScanSnap iX500:1527308\' --mode=gray --resolution=100 --format=png | zbarimg -Sdisable -Sqrcode.enable --raw - | head --bytes=-1'; end > out + +in { + inherit test; +} diff --git a/pkgs/profpatsch/gpg-private-offline-key/default.nix b/pkgs/profpatsch/gpg-private-offline-key/default.nix new file mode 100644 index 00000000..d0e5ceb7 --- /dev/null +++ b/pkgs/profpatsch/gpg-private-offline-key/default.nix @@ -0,0 +1,57 @@ +{ pkgs, writeExecline, getBins }: + +let +# split + + bins = getBins pkgs.coreutils [ "split" "mktemp" "rm" "rmdir" ] + // getBins pkgs.lr [ "lr" ] + // getBins pkgs.xe [ "xe" ] + // getBins pkgs.qrencode [ "qrencode" ] + // getBins pkgs.zbar [ "zbarimg" ] + // getBins pkgs.sane-backends [ "scanimg" ]; + + qr-code-props = { + # second highest redundancy level + level = "Q"; + # max amount of bytes that level Q can encode + bytes = "1700"; + }; + + # Takes a private GPG key encoded with paperkey on stdin + # and execs into the argv for each qr code image (on stdin). + print-qr-codes = writeExecline "print-qr-codes" {} [ + split-stdin + "pipeline" [ + bins.qrencode + "--level=${qr-code-props.level}" + "--dpi=300" + "-o-" + ] + "$@" + ]; + + split-stdin = pkgs.writers.writePython3 "split-stdin" {} '' + import sys + import subprocess + + while True: + str = sys.stdin.buffer.read(${qr-code-props.bytes}) + if str == "": + break + ret = subprocess.run(sys.argv[1:], input=str) + code = ret.returncode + if code != 0: + sys.exit(code if code > 0 else 128 - code) + ''; + + test = writeExecline "test12" {} [ + "foreground" [ print-qr-codes "feh" "-" ] + ]; + + +# scanning: +# for i in (seq 0 9); scanimage --device=\'fujitsu:ScanSnap iX500:1527308\' --mode=gray --resolution=100 --format=png | zbarimg -Sdisable -Sqrcode.enable --raw - | head --bytes=-1; end > out + +in { + inherit print-qr-codes; +} -- cgit 1.4.1