about summary refs log tree commit diff
path: root/pkgs/profpatsch/gpg-private-offline-key
diff options
context:
space:
mode:
authorProfpatsch <mail@profpatsch.de>2021-03-05 23:53:13 +0100
committerProfpatsch <mail@profpatsch.de>2021-03-05 23:53:13 +0100
commitcb8c76db0f8523e4265738df0f079555e4b530c3 (patch)
tree96c8cb461d9cad650aacbd9776180caee174a9b4 /pkgs/profpatsch/gpg-private-offline-key
parent2b182dc40d3b31887b044a45cfc90de864ff61fb (diff)
pkgs/profpatsch: add gpg-private-offline-key
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.
Diffstat (limited to 'pkgs/profpatsch/gpg-private-offline-key')
-rw-r--r--pkgs/profpatsch/gpg-private-offline-key/:77
-rw-r--r--pkgs/profpatsch/gpg-private-offline-key/default.nix57
2 files changed, 134 insertions, 0 deletions
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;
+}